From patchwork Fri Dec 13 08:36:03 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Sangorrin X-Patchwork-Id: 3347451 Return-Path: X-Original-To: patchwork-ltsi-dev@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 9CFB9C0D4A for ; Sat, 14 Dec 2013 08:07:58 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id CE9F920784 for ; Sat, 14 Dec 2013 08:07:41 +0000 (UTC) Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) by mail.kernel.org (Postfix) with ESMTP id 69285206AF for ; Sat, 14 Dec 2013 08:07:24 +0000 (UTC) Received: from mail.linux-foundation.org (localhost [IPv6:::1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 44C69B12; Sat, 14 Dec 2013 08:06:39 +0000 (UTC) X-Original-To: ltsi-dev@lists.linuxfoundation.org Delivered-To: ltsi-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTP id E4E52273 for ; Fri, 13 Dec 2013 08:37:15 +0000 (UTC) X-Greylist: from auto-whitelisted by SQLgrey-1.7.6 Received: from imx12.toshiba.co.jp (imx12.toshiba.co.jp [61.202.160.132]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 71D301F8C3 for ; Fri, 13 Dec 2013 08:36:44 +0000 (UTC) Received: from arc11.toshiba.co.jp ([133.199.90.127]) by imx12.toshiba.co.jp with ESMTP id rBD8aFIf024920; Fri, 13 Dec 2013 17:36:15 +0900 (JST) Received: (from root@localhost) by arc11.toshiba.co.jp id rBD8aFHA007733; Fri, 13 Dec 2013 17:36:15 +0900 (JST) Received: from ovp11.toshiba.co.jp [133.199.90.148] by arc11.toshiba.co.jp with ESMTP id TAA07719; Fri, 13 Dec 2013 17:36:15 +0900 Received: from mx.toshiba.co.jp (localhost [127.0.0.1]) by ovp11.toshiba.co.jp with ESMTP id rBD8aEIQ018742; Fri, 13 Dec 2013 17:36:14 +0900 (JST) Received: from BK2211.rdc.toshiba.co.jp by toshiba.co.jp id rBD8a83h029996; Fri, 13 Dec 2013 17:36:08 +0900 (JST) Received: from island.swc.toshiba.co.jp (localhost [127.0.0.1]) by BK2211.rdc.toshiba.co.jp (8.13.8+Sun/8.13.8) with ESMTP id rBD8a7fF028025; Fri, 13 Dec 2013 17:36:07 +0900 (JST) Received: from ubuntu.swc.toshiba.co.jp (unknown [133.196.174.184]) by island.swc.toshiba.co.jp (Postfix) with ESMTP id 48EF540003; Fri, 13 Dec 2013 17:35:21 +0900 (JST) From: Daniel Sangorrin To: ltsi-dev@lists.linuxfoundation.org Date: Fri, 13 Dec 2013 17:36:03 +0900 Message-Id: <1386923766-22543-2-git-send-email-daniel.sangorrin@toshiba.co.jp> X-Mailer: git-send-email 1.8.5 In-Reply-To: <1386923766-22543-1-git-send-email-daniel.sangorrin@toshiba.co.jp> References: <1386923766-22543-1-git-send-email-daniel.sangorrin@toshiba.co.jp> X-Spam-Status: No, score=-2.3 required=5.0 tests=BAYES_00,RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Mailman-Approved-At: Sat, 14 Dec 2013 08:06:34 +0000 Cc: michal.simek@xilinx.com Subject: [LTSI-dev] [PATCH 1/4] net: ethernet: xilinx: Merge driver from Xilinx repository X-BeenThere: ltsi-dev@lists.linuxfoundation.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: "A list to discuss patches, development, and other things related to the LTSI project" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ltsi-dev-bounces@lists.linuxfoundation.org Errors-To: ltsi-dev-bounces@lists.linuxfoundation.org X-Virus-Scanned: ClamAV using ClamSMTP From: Michal Simek Update the Xilinx ethernet driver used in ZYNQ to the Xilinx repository (commit 4edd65e63fe4a998164a8d7d8c8c86f4300825d7). Signed-off-by: Daniel Sangorrin Signed-off-by: Yoshitake Kobayashi --- drivers/net/ethernet/xilinx/Kconfig | 68 +- drivers/net/ethernet/xilinx/Makefile | 3 + drivers/net/ethernet/xilinx/ll_temac_main.c | 48 +- drivers/net/ethernet/xilinx/ll_temac_mdio.c | 5 +- drivers/net/ethernet/xilinx/xilinx_axienet.h | 15 +- drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 313 +- drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c | 82 +- drivers/net/ethernet/xilinx/xilinx_emaclite.c | 3 +- drivers/net/ethernet/xilinx/xilinx_emacps.c | 2922 +++++++++++++++ .../net/ethernet/xilinx/xilinx_lltemac/Makefile | 10 + .../net/ethernet/xilinx/xilinx_lltemac/xlltemac.c | 1387 +++++++ .../net/ethernet/xilinx/xilinx_lltemac/xlltemac.h | 785 ++++ .../xilinx/xilinx_lltemac/xlltemac_control.c | 679 ++++ .../ethernet/xilinx/xilinx_lltemac/xlltemac_hw.h | 562 +++ .../ethernet/xilinx/xilinx_lltemac/xlltemac_main.c | 3850 ++++++++++++++++++++ 15 files changed, 10522 insertions(+), 210 deletions(-) create mode 100644 drivers/net/ethernet/xilinx/xilinx_emacps.c create mode 100644 drivers/net/ethernet/xilinx/xilinx_lltemac/Makefile create mode 100644 drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac.c create mode 100644 drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac.h create mode 100644 drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac_control.c create mode 100644 drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac_hw.h create mode 100644 drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac_main.c diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig index 7b90a5e..0bbbe5e 100644 --- a/drivers/net/ethernet/xilinx/Kconfig +++ b/drivers/net/ethernet/xilinx/Kconfig @@ -27,18 +27,74 @@ config XILINX_EMACLITE config XILINX_AXI_EMAC tristate "Xilinx 10/100/1000 AXI Ethernet support" - depends on MICROBLAZE + depends on (MICROBLAZE || ARCH_ZYNQ) select PHYLIB ---help--- This driver supports the 10/100/1000 Ethernet from Xilinx for the AXI bus interface used in Xilinx Virtex FPGAs. -config XILINX_LL_TEMAC - tristate "Xilinx LL TEMAC (LocalLink Tri-mode Ethernet MAC) driver" - depends on (PPC || MICROBLAZE) +#config XILINX_LL_TEMAC +# tristate "Xilinx LL TEMAC (LocalLink Tri-mode Ethernet MAC) driver" +# depends on (PPC || MICROBLAZE) +# select PHYLIB +# ---help--- +# This driver supports the Xilinx 10/100/1000 LocalLink TEMAC +# core used in Xilinx Spartan and Virtex FPGAs + +config XILINX_LLTEMAC + tristate "Xilinx LLTEMAC 10/100/1000 Ethernet MAC driver" + depends on XILINX_DRIVERS + select XILINX_EDK + select NEED_XILINX_LLDMA + help + This driver supports the 10/100/1000 LLTEMAC. + +choice + prompt "Xilinx LLTEMAC PHY Support" + depends on XILINX_LLTEMAC + default XILINX_LLTEMAC_MARVELL_88E1111_GMII + +config XILINX_LLTEMAC_MARVELL_88E1111_RGMII + bool "MARVELL 88E1111 using RGMII" + help + This phy is used by many Xilinx boards. This option includes + code for enabling RGMII over copper. + +config XILINX_LLTEMAC_MARVELL_88E1111_GMII + bool "MARVELL 88E1111 using GMII" + help + This phy is used by many Xilinx boards. This option includes + code for enabling GMII over copper, and for setting the correct + speed based on whatever the phy is able to autonegotiate. This is + usually the best option to use on ML40x and ML50x boards. + +config XILINX_LLTEMAC_MARVELL_88E1111_MII + bool "MARVELL 88E1111 using MII or other PHY" + help + If your physical interface is not covered by the other + selections, then choose this option. This option includes generic + speed autonegotation code. + +config XILINX_LLTEMAC_XILINX_1000BASEX + bool "Xilinx 1000BASE-X PHY" + help + This PHY supports physical attachment via GT/GTP/GTX transceivers. + +endchoice + +config XILINX_PS_EMAC + tristate "Xilinx PS tri-speed EMAC support" + depends on ARCH_ZYNQ select PHYLIB ---help--- - This driver supports the Xilinx 10/100/1000 LocalLink TEMAC - core used in Xilinx Spartan and Virtex FPGAs + This driver supports tri-speed EMAC. + +config XILINX_PS_EMAC_HWTSTAMP + bool "Generate hardware packet timestamps" + depends on XILINX_PS_EMAC + default n + ---help--- + Generate hardare packet timestamps. This is to facilitate IEE 1588. + endif # NET_VENDOR_XILINX diff --git a/drivers/net/ethernet/xilinx/Makefile b/drivers/net/ethernet/xilinx/Makefile index 214205e..4910455 100644 --- a/drivers/net/ethernet/xilinx/Makefile +++ b/drivers/net/ethernet/xilinx/Makefile @@ -5,5 +5,8 @@ ll_temac-objs := ll_temac_main.o ll_temac_mdio.o obj-$(CONFIG_XILINX_LL_TEMAC) += ll_temac.o obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o +obj-$(CONFIG_XILINX_PS_EMAC) += xilinx_emacps.o xilinx_emac-objs := xilinx_axienet_main.o xilinx_axienet_mdio.o obj-$(CONFIG_XILINX_AXI_EMAC) += xilinx_emac.o + +obj-$(CONFIG_XILINX_LLTEMAC) += xilinx_lltemac/ diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index 1f23641..62dcf42 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -244,15 +243,15 @@ static int temac_dma_bd_init(struct net_device *ndev) /* allocate the tx and rx ring buffer descriptors. */ /* returns a virtual address and a physical address. */ - lp->tx_bd_v = dma_zalloc_coherent(ndev->dev.parent, - sizeof(*lp->tx_bd_v) * TX_BD_NUM, - &lp->tx_bd_p, GFP_KERNEL); + lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent, + sizeof(*lp->tx_bd_v) * TX_BD_NUM, + &lp->tx_bd_p, GFP_KERNEL | __GFP_ZERO); if (!lp->tx_bd_v) goto out; - lp->rx_bd_v = dma_zalloc_coherent(ndev->dev.parent, - sizeof(*lp->rx_bd_v) * RX_BD_NUM, - &lp->rx_bd_p, GFP_KERNEL); + lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent, + sizeof(*lp->rx_bd_v) * RX_BD_NUM, + &lp->rx_bd_p, GFP_KERNEL | __GFP_ZERO); if (!lp->rx_bd_v) goto out; @@ -298,12 +297,6 @@ static int temac_dma_bd_init(struct net_device *ndev) lp->rx_bd_p + (sizeof(*lp->rx_bd_v) * (RX_BD_NUM - 1))); lp->dma_out(lp, TX_CURDESC_PTR, lp->tx_bd_p); - /* Init descriptor indexes */ - lp->tx_bd_ci = 0; - lp->tx_bd_next = 0; - lp->tx_bd_tail = 0; - lp->rx_bd_ci = 0; - return 0; out: @@ -685,15 +678,12 @@ static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev) skb_frag_t *frag; num_frag = skb_shinfo(skb)->nr_frags; - frag = &skb_shinfo(skb)->frags[0]; start_p = lp->tx_bd_p + sizeof(*lp->tx_bd_v) * lp->tx_bd_tail; cur_p = &lp->tx_bd_v[lp->tx_bd_tail]; if (temac_check_tx_bd_space(lp, num_frag)) { - if (!netif_queue_stopped(ndev)) { + if (!netif_queue_stopped(ndev)) netif_stop_queue(ndev); - return NETDEV_TX_BUSY; - } return NETDEV_TX_BUSY; } @@ -709,11 +699,12 @@ static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev) cur_p->app0 |= STS_CTRL_APP0_SOP; cur_p->len = skb_headlen(skb); - cur_p->phys = dma_map_single(ndev->dev.parent, skb->data, skb->len, - DMA_TO_DEVICE); + cur_p->phys = dma_map_single(ndev->dev.parent, skb->data, + skb_headlen(skb), DMA_TO_DEVICE); cur_p->app4 = (unsigned long)skb; for (ii = 0; ii < num_frag; ii++) { + frag = &skb_shinfo(skb)->frags[ii]; lp->tx_bd_tail++; if (lp->tx_bd_tail >= TX_BD_NUM) lp->tx_bd_tail = 0; @@ -724,7 +715,6 @@ static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev) skb_frag_size(frag), DMA_TO_DEVICE); cur_p->len = skb_frag_size(frag); cur_p->app0 = 0; - frag++; } cur_p->app0 |= STS_CTRL_APP0_EOP; @@ -1014,7 +1004,7 @@ static int temac_of_probe(struct platform_device *op) return -ENOMEM; ether_setup(ndev); - platform_set_drvdata(op, ndev); + dev_set_drvdata(&op->dev, ndev); SET_NETDEV_DEV(ndev, &op->dev); ndev->flags &= ~IFF_MULTICAST; /* clear multicast */ ndev->features = NETIF_F_SG | NETIF_F_FRAGLIST; @@ -1052,12 +1042,14 @@ static int temac_of_probe(struct platform_device *op) /* Setup checksum offload, but default to off if not specified */ lp->temac_features = 0; p = (__be32 *)of_get_property(op->dev.of_node, "xlnx,txcsum", NULL); + dev_info(&op->dev, "TX_CSUM %d\n", be32_to_cpup(p)); if (p && be32_to_cpu(*p)) { lp->temac_features |= TEMAC_FEATURE_TX_CSUM; /* Can checksum TCP/UDP over IPv4. */ ndev->features |= NETIF_F_IP_CSUM; } p = (__be32 *)of_get_property(op->dev.of_node, "xlnx,rxcsum", NULL); + dev_info(&op->dev, "RX_CSUM %d\n", be32_to_cpup(p)); if (p && be32_to_cpu(*p)) lp->temac_features |= TEMAC_FEATURE_RX_CSUM; @@ -1105,14 +1097,15 @@ static int temac_of_probe(struct platform_device *op) } temac_init_mac_address(ndev, (void *)addr); - rc = temac_mdio_setup(lp, op->dev.of_node); - if (rc) - dev_warn(&op->dev, "error registering MDIO bus\n"); - lp->phy_node = of_parse_phandle(op->dev.of_node, "phy-handle", 0); - if (lp->phy_node) + if (lp->phy_node) { dev_dbg(lp->dev, "using PHY node %s (%p)\n", np->full_name, np); + rc = temac_mdio_setup(lp, op->dev.of_node); + if (rc) + dev_warn(&op->dev, "error registering MDIO bus\n"); + } + /* Add the device attributes */ rc = sysfs_create_group(&lp->dev->kobj, &temac_attr_group); if (rc) { @@ -1143,7 +1136,7 @@ static int temac_of_probe(struct platform_device *op) static int temac_of_remove(struct platform_device *op) { - struct net_device *ndev = platform_get_drvdata(op); + struct net_device *ndev = dev_get_drvdata(&op->dev); struct temac_local *lp = netdev_priv(ndev); temac_mdio_teardown(lp); @@ -1152,6 +1145,7 @@ static int temac_of_remove(struct platform_device *op) if (lp->phy_node) of_node_put(lp->phy_node); lp->phy_node = NULL; + dev_set_drvdata(&op->dev, NULL); iounmap(lp->regs); if (lp->sdma_regs) iounmap(lp->sdma_regs); diff --git a/drivers/net/ethernet/xilinx/ll_temac_mdio.c b/drivers/net/ethernet/xilinx/ll_temac_mdio.c index 8cf9d4f..634d898 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_mdio.c +++ b/drivers/net/ethernet/xilinx/ll_temac_mdio.c @@ -63,6 +63,7 @@ int temac_mdio_setup(struct temac_local *lp, struct device_node *np) int clk_div; int rc, size; struct resource res; + struct device_node *np1 = of_get_parent(lp->phy_node); /* Calculate a reasonable divisor for the clock rate */ clk_div = 0x3f; /* worst-case default setting */ @@ -85,7 +86,7 @@ int temac_mdio_setup(struct temac_local *lp, struct device_node *np) if (!bus) return -ENOMEM; - of_address_to_resource(np, 0, &res); + of_address_to_resource(np1, 0, &res); snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", (unsigned long long)res.start); bus->priv = lp; @@ -97,7 +98,7 @@ int temac_mdio_setup(struct temac_local *lp, struct device_node *np) lp->mii_bus = bus; - rc = of_mdiobus_register(bus, np); + rc = of_mdiobus_register(bus, np1); if (rc) goto err_register; diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h index 44b8d2b..738beb2 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h @@ -9,18 +9,19 @@ #define XILINX_AXIENET_H #include +#include #include #include +#include /* Packet size info */ #define XAE_HDR_SIZE 14 /* Size of Ethernet header */ -#define XAE_HDR_VLAN_SIZE 18 /* Size of an Ethernet hdr + VLAN */ #define XAE_TRL_SIZE 4 /* Size of Ethernet trailer (FCS) */ #define XAE_MTU 1500 /* Max MTU of an Ethernet frame */ #define XAE_JUMBO_MTU 9000 /* Max MTU of a jumbo Eth. frame */ #define XAE_MAX_FRAME_SIZE (XAE_MTU + XAE_HDR_SIZE + XAE_TRL_SIZE) -#define XAE_MAX_VLAN_FRAME_SIZE (XAE_MTU + XAE_HDR_VLAN_SIZE + XAE_TRL_SIZE) +#define XAE_MAX_VLAN_FRAME_SIZE (XAE_MTU + VLAN_ETH_HLEN + XAE_TRL_SIZE) #define XAE_MAX_JUMBO_FRAME_SIZE (XAE_JUMBO_MTU + XAE_HDR_SIZE + XAE_TRL_SIZE) /* Configuration options */ @@ -337,6 +338,14 @@ #define DELAY_OF_ONE_MILLISEC 1000 +/* Read/Write access to the registers */ +#ifndef out_be32 +#ifdef CONFIG_ARCH_ZYNQ +#define in_be32(offset) __raw_readl(offset) +#define out_be32(offset, val) __raw_writel(val, offset) +#endif +#endif + /** * struct axidma_bd - Axi Dma buffer descriptor layout * @next: MM2S/S2MM Next Descriptor Pointer @@ -448,7 +457,7 @@ struct axienet_local { u32 rx_bd_ci; u32 max_frm_size; - u32 jumbo_support; + u32 rxmem; int csum_offload_on_tx_path; int csum_offload_on_rx_path; diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index b2ff038..4caa5b9 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -201,15 +201,17 @@ static int axienet_dma_bd_init(struct net_device *ndev) /* * Allocate the Tx and Rx buffer descriptors. */ - lp->tx_bd_v = dma_zalloc_coherent(ndev->dev.parent, - sizeof(*lp->tx_bd_v) * TX_BD_NUM, - &lp->tx_bd_p, GFP_KERNEL); + lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent, + sizeof(*lp->tx_bd_v) * TX_BD_NUM, + &lp->tx_bd_p, + GFP_KERNEL | __GFP_ZERO); if (!lp->tx_bd_v) goto out; - lp->rx_bd_v = dma_zalloc_coherent(ndev->dev.parent, - sizeof(*lp->rx_bd_v) * RX_BD_NUM, - &lp->rx_bd_p, GFP_KERNEL); + lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent, + sizeof(*lp->rx_bd_v) * RX_BD_NUM, + &lp->rx_bd_p, + GFP_KERNEL | __GFP_ZERO); if (!lp->rx_bd_v) goto out; @@ -263,7 +265,8 @@ static int axienet_dma_bd_init(struct net_device *ndev) axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr); /* Populate the tail pointer and bring the Rx Axi DMA engine out of - * halted state. This will make the Rx side ready for reception.*/ + * halted state. This will make the Rx side ready for reception. + */ axienet_dma_out32(lp, XAXIDMA_RX_CDESC_OFFSET, lp->rx_bd_p); cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, @@ -273,7 +276,8 @@ static int axienet_dma_bd_init(struct net_device *ndev) /* Write to the RS (Run-stop) bit in the Tx channel control register. * Tx channel is now ready to run. But only after we write to the - * tail pointer register that the Tx channel will start transmitting */ + * tail pointer register that the Tx channel will start transmitting. + */ axienet_dma_out32(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p); cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, @@ -354,7 +358,8 @@ static void axienet_set_multicast_list(struct net_device *ndev) netdev_mc_count(ndev) > XAE_MULTICAST_CAM_TABLE_NUM) { /* We must make the kernel realize we had to move into * promiscuous mode. If it was a promiscuous mode request - * the flag is already set. If not we set it. */ + * the flag is already set. If not we set it. + */ ndev->flags |= IFF_PROMISC; reg = axienet_ior(lp, XAE_FMI_OFFSET); reg |= XAE_FMI_PM_MASK; @@ -438,14 +443,15 @@ static void __axienet_device_reset(struct axienet_local *lp, /* Reset Axi DMA. This would reset Axi Ethernet core as well. The reset * process of Axi DMA takes a while to complete as all pending * commands/transfers will be flushed or completed during this - * reset process. */ + * reset process. + */ axienet_dma_out32(lp, offset, XAXIDMA_CR_RESET_MASK); timeout = DELAY_OF_ONE_MILLISEC; while (axienet_dma_in32(lp, offset) & XAXIDMA_CR_RESET_MASK) { udelay(1); if (--timeout == 0) { - dev_err(dev, "axienet_device_reset DMA " - "reset timeout!\n"); + dev_err(dev, + "axienet_device_reset DMA reset timeout!\n"); break; } } @@ -471,18 +477,21 @@ static void axienet_device_reset(struct net_device *ndev) __axienet_device_reset(lp, &ndev->dev, XAXIDMA_RX_CR_OFFSET); lp->max_frm_size = XAE_MAX_VLAN_FRAME_SIZE; + lp->options |= XAE_OPTION_VLAN; lp->options &= (~XAE_OPTION_JUMBO); if ((ndev->mtu > XAE_MTU) && - (ndev->mtu <= XAE_JUMBO_MTU) && - (lp->jumbo_support)) { - lp->max_frm_size = ndev->mtu + XAE_HDR_VLAN_SIZE + - XAE_TRL_SIZE; - lp->options |= XAE_OPTION_JUMBO; + (ndev->mtu <= XAE_JUMBO_MTU)) { + lp->max_frm_size = ndev->mtu + VLAN_ETH_HLEN + + XAE_TRL_SIZE; + + if (lp->max_frm_size <= lp->rxmem) + lp->options |= XAE_OPTION_JUMBO; } if (axienet_dma_bd_init(ndev)) { - dev_err(&ndev->dev, "axienet_device_reset descriptor " + dev_err(&ndev->dev, + "axienet_device_reset descriptor " "allocation failed\n"); } @@ -497,7 +506,8 @@ static void axienet_device_reset(struct net_device *ndev) axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK); /* Sync default options with HW but leave receiver and - * transmitter disabled.*/ + * transmitter disabled. + */ axienet_setoptions(ndev, lp->options & ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN)); axienet_set_mac_address(ndev, NULL); @@ -549,7 +559,8 @@ static void axienet_adjust_link(struct net_device *ndev) emmc_reg |= XAE_EMMC_LINKSPD_10; break; default: - dev_err(&ndev->dev, "Speed other than 10, 100 " + dev_err(&ndev->dev, + "Speed other than 10, 100 " "or 1Gbps is not supported\n"); break; } @@ -558,8 +569,8 @@ static void axienet_adjust_link(struct net_device *ndev) lp->last_link = link_state; phy_print_status(phy); } else { - dev_err(&ndev->dev, "Error setting Axi Ethernet " - "mac speed\n"); + dev_err(&ndev->dev, + "Error setting Axi Ethernet mac speed\n"); } } } @@ -601,7 +612,8 @@ static void axienet_start_xmit_done(struct net_device *ndev) size += status & XAXIDMA_BD_STS_ACTUAL_LEN_MASK; packets++; - lp->tx_bd_ci = ++lp->tx_bd_ci % TX_BD_NUM; + ++lp->tx_bd_ci; + lp->tx_bd_ci %= TX_BD_NUM; cur_p = &lp->tx_bd_v[lp->tx_bd_ci]; status = cur_p->status; } @@ -687,7 +699,8 @@ static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev) skb_headlen(skb), DMA_TO_DEVICE); for (ii = 0; ii < num_frag; ii++) { - lp->tx_bd_tail = ++lp->tx_bd_tail % TX_BD_NUM; + ++lp->tx_bd_tail; + lp->tx_bd_tail %= TX_BD_NUM; cur_p = &lp->tx_bd_v[lp->tx_bd_tail]; frag = &skb_shinfo(skb)->frags[ii]; cur_p->phys = dma_map_single(ndev->dev.parent, @@ -701,9 +714,12 @@ static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev) cur_p->app4 = (unsigned long)skb; tail_p = lp->tx_bd_p + sizeof(*lp->tx_bd_v) * lp->tx_bd_tail; + wmb(); + /* Start the transfer */ axienet_dma_out32(lp, XAXIDMA_TX_TDESC_OFFSET, tail_p); - lp->tx_bd_tail = ++lp->tx_bd_tail % TX_BD_NUM; + ++lp->tx_bd_tail; + lp->tx_bd_tail %= TX_BD_NUM; return NETDEV_TX_OK; } @@ -723,15 +739,16 @@ static void axienet_recv(struct net_device *ndev) u32 csumstatus; u32 size = 0; u32 packets = 0; - dma_addr_t tail_p; + dma_addr_t tail_p = 0; struct axienet_local *lp = netdev_priv(ndev); struct sk_buff *skb, *new_skb; struct axidma_bd *cur_p; - tail_p = lp->rx_bd_p + sizeof(*lp->rx_bd_v) * lp->rx_bd_ci; + rmb(); cur_p = &lp->rx_bd_v[lp->rx_bd_ci]; while ((cur_p->status & XAXIDMA_BD_STS_COMPLETE_MASK)) { + tail_p = lp->rx_bd_p + sizeof(*lp->rx_bd_v) * lp->rx_bd_ci; skb = (struct sk_buff *) (cur_p->sw_id_offset); length = cur_p->app4 & 0x0000FFFF; @@ -775,14 +792,16 @@ static void axienet_recv(struct net_device *ndev) cur_p->status = 0; cur_p->sw_id_offset = (u32) new_skb; - lp->rx_bd_ci = ++lp->rx_bd_ci % RX_BD_NUM; + ++lp->rx_bd_ci; + lp->rx_bd_ci %= RX_BD_NUM; cur_p = &lp->rx_bd_v[lp->rx_bd_ci]; } ndev->stats.rx_packets += packets; ndev->stats.rx_bytes += size; - axienet_dma_out32(lp, XAXIDMA_RX_TDESC_OFFSET, tail_p); + if (tail_p) + axienet_dma_out32(lp, XAXIDMA_RX_TDESC_OFFSET, tail_p); } /** @@ -804,6 +823,7 @@ static irqreturn_t axienet_tx_irq(int irq, void *_ndev) status = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET); if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) { + axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status); axienet_start_xmit_done(lp->ndev); goto out; } @@ -827,9 +847,9 @@ static irqreturn_t axienet_tx_irq(int irq, void *_ndev) axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr); tasklet_schedule(&lp->dma_err_tasklet); + axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status); } out: - axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status); return IRQ_HANDLED; } @@ -852,6 +872,7 @@ static irqreturn_t axienet_rx_irq(int irq, void *_ndev) status = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET); if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) { + axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status); axienet_recv(lp->ndev); goto out; } @@ -875,9 +896,9 @@ static irqreturn_t axienet_rx_irq(int irq, void *_ndev) axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr); tasklet_schedule(&lp->dma_err_tasklet); + axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status); } out: - axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status); return IRQ_HANDLED; } @@ -910,7 +931,8 @@ static int axienet_open(struct net_device *ndev) /* Disable the MDIO interface till Axi Ethernet Reset is completed. * When we do an Axi Ethernet reset, it resets the complete core * including the MDIO. If MDIO is not disabled when the reset - * process is started, MDIO will be broken afterwards. */ + * process is started, MDIO will be broken afterwards. + */ axienet_iow(lp, XAE_MDIO_MC_OFFSET, (mdio_mcreg & (~XAE_MDIO_MC_MDIOEN_MASK))); axienet_device_reset(ndev); @@ -921,14 +943,20 @@ static int axienet_open(struct net_device *ndev) return ret; if (lp->phy_node) { - lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node, + if (lp->phy_type == XAE_PHY_TYPE_GMII) { + lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node, axienet_adjust_link, 0, PHY_INTERFACE_MODE_GMII); - if (!lp->phy_dev) { - dev_err(lp->dev, "of_phy_connect() failed\n"); - return -ENODEV; + } else if (lp->phy_type == XAE_PHY_TYPE_RGMII_2_0) { + lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node, + axienet_adjust_link, 0, + PHY_INTERFACE_MODE_RGMII_ID); } - phy_start(lp->phy_dev); + + if (!lp->phy_dev) + dev_err(lp->dev, "of_phy_connect() failed\n"); + else + phy_start(lp->phy_dev); } /* Enable tasklets for Axi DMA error handling */ @@ -1013,15 +1041,15 @@ static int axienet_change_mtu(struct net_device *ndev, int new_mtu) if (netif_running(ndev)) return -EBUSY; - if (lp->jumbo_support) { - if ((new_mtu > XAE_JUMBO_MTU) || (new_mtu < 64)) - return -EINVAL; - ndev->mtu = new_mtu; - } else { - if ((new_mtu > XAE_MTU) || (new_mtu < 64)) - return -EINVAL; - ndev->mtu = new_mtu; - } + + if ((new_mtu + VLAN_ETH_HLEN + + XAE_TRL_SIZE) > lp->rxmem) + return -EINVAL; + + if ((new_mtu > XAE_JUMBO_MTU) || (new_mtu < 64)) + return -EINVAL; + + ndev->mtu = new_mtu; return 0; } @@ -1046,6 +1074,20 @@ static void axienet_poll_controller(struct net_device *ndev) } #endif +/* Ioctl MII Interface */ +static int axienet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct axienet_local *priv = netdev_priv(dev); + + if (!netif_running(dev)) + return -EINVAL; + + if (!priv->phy_dev) + return -EOPNOTSUPP; + + return phy_mii_ioctl(priv->phy_dev, rq, cmd); +} + static const struct net_device_ops axienet_netdev_ops = { .ndo_open = axienet_open, .ndo_stop = axienet_stop, @@ -1054,6 +1096,7 @@ static const struct net_device_ops axienet_netdev_ops = { .ndo_set_mac_address = netdev_set_mac_address, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = axienet_set_multicast_list, + .ndo_do_ioctl = axienet_ioctl, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = axienet_poll_controller, #endif @@ -1209,7 +1252,7 @@ axienet_ethtools_get_pauseparam(struct net_device *ndev, * axienet_ethtools_set_pauseparam - Set device pause parameter(flow control) * settings. * @ndev: Pointer to net_device structure - * @epauseparam:Pointer to ethtool_pauseparam structure + * @epauseparm:Pointer to ethtool_pauseparam structure * * This implements ethtool command for enabling flow control on Rx and Tx * paths. Issue "ethtool -A ethX tx on|off" under linux prompt to execute this @@ -1223,8 +1266,9 @@ axienet_ethtools_set_pauseparam(struct net_device *ndev, struct axienet_local *lp = netdev_priv(ndev); if (netif_running(ndev)) { - printk(KERN_ERR "%s: Please stop netif before applying " - "configruation\n", ndev->name); + dev_err(&ndev->dev, + "%s: Please stop netif before applying configuration\n", + ndev->name); return -EFAULT; } @@ -1280,8 +1324,9 @@ static int axienet_ethtools_set_coalesce(struct net_device *ndev, struct axienet_local *lp = netdev_priv(ndev); if (netif_running(ndev)) { - printk(KERN_ERR "%s: Please stop netif before applying " - "configruation\n", ndev->name); + dev_err(&ndev->dev, + "%s: Please stop netif before applying configuration\n", + ndev->name); return -EFAULT; } @@ -1350,7 +1395,8 @@ static void axienet_dma_err_handler(unsigned long data) /* Disable the MDIO interface till Axi Ethernet Reset is completed. * When we do an Axi Ethernet reset, it resets the complete core * including the MDIO. So if MDIO is not disabled when the reset - * process is started, MDIO will be broken afterwards. */ + * process is started, MDIO will be broken afterwards. + */ axienet_iow(lp, XAE_MDIO_MC_OFFSET, (mdio_mcreg & ~XAE_MDIO_MC_MDIOEN_MASK)); @@ -1421,7 +1467,8 @@ static void axienet_dma_err_handler(unsigned long data) axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr); /* Populate the tail pointer and bring the Rx Axi DMA engine out of - * halted state. This will make the Rx side ready for reception.*/ + * halted state. This will make the Rx side ready for reception. + */ axienet_dma_out32(lp, XAXIDMA_RX_CDESC_OFFSET, lp->rx_bd_p); cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, @@ -1431,7 +1478,8 @@ static void axienet_dma_err_handler(unsigned long data) /* Write to the RS (Run-stop) bit in the Tx channel control register. * Tx channel is now ready to run. But only after we write to the - * tail pointer register that the Tx channel will start transmitting */ + * tail pointer register that the Tx channel will start transmitting + */ axienet_dma_out32(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p); cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, @@ -1447,7 +1495,8 @@ static void axienet_dma_err_handler(unsigned long data) axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK); /* Sync default options with HW but leave receiver and - * transmitter disabled.*/ + * transmitter disabled. + */ axienet_setoptions(ndev, lp->options & ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN)); axienet_set_mac_address(ndev, NULL); @@ -1456,9 +1505,8 @@ static void axienet_dma_err_handler(unsigned long data) } /** - * axienet_of_probe - Axi Ethernet probe function. - * @op: Pointer to platform device structure. - * @match: Pointer to device id structure + * axienet_probe - Axi Ethernet probe function. + * @pdev: Pointer to platform device structure. * * returns: 0, on success * Non-zero error value on failure. @@ -1468,65 +1516,73 @@ static void axienet_dma_err_handler(unsigned long data) * device. Parses through device tree and populates fields of * axienet_local. It registers the Ethernet device. */ -static int axienet_of_probe(struct platform_device *op) +static int axienet_probe(struct platform_device *pdev) { - __be32 *p; - int size, ret = 0; + int ret; struct device_node *np; struct axienet_local *lp; struct net_device *ndev; - const void *addr; + u8 mac_addr[6]; + struct resource *ethres, dmares; + u32 value; ndev = alloc_etherdev(sizeof(*lp)); if (!ndev) return -ENOMEM; ether_setup(ndev); - platform_set_drvdata(op, ndev); + platform_set_drvdata(pdev, ndev); - SET_NETDEV_DEV(ndev, &op->dev); + SET_NETDEV_DEV(ndev, &pdev->dev); ndev->flags &= ~IFF_MULTICAST; /* clear multicast */ - ndev->features = NETIF_F_SG | NETIF_F_FRAGLIST; + ndev->features = NETIF_F_FRAGLIST; ndev->netdev_ops = &axienet_netdev_ops; ndev->ethtool_ops = &axienet_ethtool_ops; lp = netdev_priv(ndev); lp->ndev = ndev; - lp->dev = &op->dev; + lp->dev = &pdev->dev; lp->options = XAE_OPTION_DEFAULTS; /* Map device registers */ - lp->regs = of_iomap(op->dev.of_node, 0); + ethres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + lp->regs = devm_ioremap_resource(&pdev->dev, ethres); if (!lp->regs) { - dev_err(&op->dev, "could not map Axi Ethernet regs.\n"); - goto nodev; + dev_err(&pdev->dev, "could not map Axi Ethernet regs.\n"); + ret = -ENOMEM; + goto free_netdev; } + /* Setup checksum offload, but default to off if not specified */ lp->features = 0; - p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,txcsum", NULL); - if (p) { - switch (be32_to_cpup(p)) { + ret = of_property_read_u32(pdev->dev.of_node, "xlnx,txcsum", &value); + if (!ret) { + dev_info(&pdev->dev, "TX_CSUM %d\n", value); + + switch (value) { case 1: lp->csum_offload_on_tx_path = XAE_FEATURE_PARTIAL_TX_CSUM; lp->features |= XAE_FEATURE_PARTIAL_TX_CSUM; /* Can checksum TCP/UDP over IPv4. */ - ndev->features |= NETIF_F_IP_CSUM; + ndev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; break; case 2: lp->csum_offload_on_tx_path = XAE_FEATURE_FULL_TX_CSUM; lp->features |= XAE_FEATURE_FULL_TX_CSUM; /* Can checksum TCP/UDP over IPv4. */ - ndev->features |= NETIF_F_IP_CSUM; + ndev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; break; default: lp->csum_offload_on_tx_path = XAE_NO_CSUM_OFFLOAD; } } - p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,rxcsum", NULL); - if (p) { - switch (be32_to_cpup(p)) { + ret = of_property_read_u32(pdev->dev.of_node, "xlnx,rxcsum", &value); + if (!ret) { + dev_info(&pdev->dev, "RX_CSUM %d\n", value); + + switch (value) { case 1: lp->csum_offload_on_rx_path = XAE_FEATURE_PARTIAL_RX_CSUM; @@ -1542,85 +1598,79 @@ static int axienet_of_probe(struct platform_device *op) } } /* For supporting jumbo frames, the Axi Ethernet hardware must have - * a larger Rx/Tx Memory. Typically, the size must be more than or - * equal to 16384 bytes, so that we can enable jumbo option and start - * supporting jumbo frames. Here we check for memory allocated for - * Rx/Tx in the hardware from the device-tree and accordingly set - * flags. */ - p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,rxmem", NULL); - if (p) { - if ((be32_to_cpup(p)) >= 0x4000) - lp->jumbo_support = 1; - } - p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,temac-type", - NULL); - if (p) - lp->temac_type = be32_to_cpup(p); - p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,phy-type", NULL); - if (p) - lp->phy_type = be32_to_cpup(p); + * a larger Rx/Tx Memory. Typically, the size must be large so that + * we can enable jumbo option and start supporting jumbo frames. + * Here we check for memory allocated for Rx/Tx in the hardware from + * the device-tree and accordingly set flags. + */ + of_property_read_u32(pdev->dev.of_node, "xlnx,rxmem", &lp->rxmem); + of_property_read_u32(pdev->dev.of_node, "xlnx,temac-type", + &lp->temac_type); + of_property_read_u32(pdev->dev.of_node, "xlnx,phy-type", &lp->phy_type); /* Find the DMA node, map the DMA registers, and decode the DMA IRQs */ - np = of_parse_phandle(op->dev.of_node, "axistream-connected", 0); - if (!np) { - dev_err(&op->dev, "could not find DMA node\n"); - goto err_iounmap; + np = of_parse_phandle(pdev->dev.of_node, "axistream-connected", 0); + if (IS_ERR(np)) { + dev_err(&pdev->dev, "could not find DMA node\n"); + ret = PTR_ERR(np); + goto free_netdev; } - lp->dma_regs = of_iomap(np, 0); - if (lp->dma_regs) { - dev_dbg(&op->dev, "MEM base: %p\n", lp->dma_regs); - } else { - dev_err(&op->dev, "unable to map DMA registers\n"); - of_node_put(np); + ret = of_address_to_resource(np, 0, &dmares); + if (ret) { + dev_err(&pdev->dev, "unable to get DMA resource\n"); + goto free_netdev; + } + lp->dma_regs = devm_ioremap_resource(&pdev->dev, &dmares); + if (!lp->dma_regs) { + dev_err(&pdev->dev, "could not map DMA regs\n"); + ret = -ENOMEM; + goto free_netdev; } lp->rx_irq = irq_of_parse_and_map(np, 1); lp->tx_irq = irq_of_parse_and_map(np, 0); of_node_put(np); if ((lp->rx_irq <= 0) || (lp->tx_irq <= 0)) { - dev_err(&op->dev, "could not determine irqs\n"); + dev_err(&pdev->dev, "could not determine irqs\n"); ret = -ENOMEM; - goto err_iounmap_2; + goto free_netdev; } /* Retrieve the MAC address */ - addr = of_get_property(op->dev.of_node, "local-mac-address", &size); - if ((!addr) || (size != 6)) { - dev_err(&op->dev, "could not find MAC address\n"); - ret = -ENODEV; - goto err_iounmap_2; + ret = of_property_read_u8_array(pdev->dev.of_node, + "local-mac-address", mac_addr, 6); + if (ret) { + dev_err(&pdev->dev, "could not find MAC address\n"); + goto free_netdev; } - axienet_set_mac_address(ndev, (void *) addr); + axienet_set_mac_address(ndev, (void *) mac_addr); lp->coalesce_count_rx = XAXIDMA_DFT_RX_THRESHOLD; lp->coalesce_count_tx = XAXIDMA_DFT_TX_THRESHOLD; - lp->phy_node = of_parse_phandle(op->dev.of_node, "phy-handle", 0); - ret = axienet_mdio_setup(lp, op->dev.of_node); - if (ret) - dev_warn(&op->dev, "error registering MDIO bus\n"); + lp->phy_node = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0); + if (lp->phy_node) { + ret = axienet_mdio_setup(lp, pdev->dev.of_node); + if (ret) + dev_warn(&pdev->dev, "error registering MDIO bus\n"); + } ret = register_netdev(lp->ndev); if (ret) { dev_err(lp->dev, "register_netdev() error (%i)\n", ret); - goto err_iounmap_2; + goto free_netdev; } return 0; -err_iounmap_2: - if (lp->dma_regs) - iounmap(lp->dma_regs); -err_iounmap: - iounmap(lp->regs); -nodev: +free_netdev: free_netdev(ndev); - ndev = NULL; + return ret; } -static int axienet_of_remove(struct platform_device *op) +static int axienet_remove(struct platform_device *pdev) { - struct net_device *ndev = platform_get_drvdata(op); + struct net_device *ndev = platform_get_drvdata(pdev); struct axienet_local *lp = netdev_priv(ndev); axienet_mdio_teardown(lp); @@ -1630,17 +1680,14 @@ static int axienet_of_remove(struct platform_device *op) of_node_put(lp->phy_node); lp->phy_node = NULL; - iounmap(lp->regs); - if (lp->dma_regs) - iounmap(lp->dma_regs); free_netdev(ndev); return 0; } -static struct platform_driver axienet_of_driver = { - .probe = axienet_of_probe, - .remove = axienet_of_remove, +static struct platform_driver axienet_driver = { + .probe = axienet_probe, + .remove = axienet_remove, .driver = { .owner = THIS_MODULE, .name = "xilinx_axienet", @@ -1648,7 +1695,7 @@ static struct platform_driver axienet_of_driver = { }, }; -module_platform_driver(axienet_of_driver); +module_platform_driver(axienet_driver); MODULE_DESCRIPTION("Xilinx Axi Ethernet driver"); MODULE_AUTHOR("Xilinx"); diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c index 64b4639..898d969 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c @@ -128,11 +128,12 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg, int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np) { int ret; - u32 clk_div, host_clock; - u32 *property_p; + u32 clk_div; struct mii_bus *bus; struct resource res; struct device_node *np1; + /* the ethernet controller device node */ + struct device_node *npp = NULL; /* clk_div can be calculated by deriving it from the equation: * fMDIO = fHOST / ((1 + clk_div) * 2) @@ -158,42 +159,49 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np) * fHOST can be read from the flattened device tree as property * "clock-frequency" from the CPU */ - - np1 = of_find_node_by_name(NULL, "cpu"); - if (!np1) { - printk(KERN_WARNING "%s(): Could not find CPU device node.", - __func__); - printk(KERN_WARNING "Setting MDIO clock divisor to " - "default %d\n", DEFAULT_CLOCK_DIVISOR); - clk_div = DEFAULT_CLOCK_DIVISOR; - goto issue; - } - property_p = (u32 *) of_get_property(np1, "clock-frequency", NULL); - if (!property_p) { - printk(KERN_WARNING "%s(): Could not find CPU property: " - "clock-frequency.", __func__); - printk(KERN_WARNING "Setting MDIO clock divisor to " - "default %d\n", DEFAULT_CLOCK_DIVISOR); + np1 = of_get_parent(lp->phy_node); + if (np1) + npp = of_get_parent(np1); + if (!npp) { + dev_warn(lp->dev, + "Could not find ethernet controller device node."); + dev_warn(lp->dev, "Setting MDIO clock divisor to default %d\n", + DEFAULT_CLOCK_DIVISOR); clk_div = DEFAULT_CLOCK_DIVISOR; - of_node_put(np1); - goto issue; + } else { + u32 *property_p; + + property_p = (uint32_t *)of_get_property(npp, + "clock-frequency", NULL); + if (!property_p) { + dev_warn(lp->dev, + "Could not find clock ethernet " + "controller property."); + dev_warn(lp->dev, + "Setting MDIO clock divisor to default %d\n", + DEFAULT_CLOCK_DIVISOR); + clk_div = DEFAULT_CLOCK_DIVISOR; + } else { + u32 host_clock = be32_to_cpup(property_p); + + clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1; + + /* If there is any remainder from the division of + * fHOST / (MAX_MDIO_FREQ * 2), then we need to add 1 + * to the clock divisor or we will surely be + * above 2.5 MHz + */ + if (host_clock % (MAX_MDIO_FREQ * 2)) + clk_div++; + dev_dbg(lp->dev, + "Setting MDIO clock divisor to %u " + "based on %u Hz host clock.\n", + clk_div, host_clock); + } } - host_clock = be32_to_cpup(property_p); - clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1; - /* If there is any remainder from the division of - * fHOST / (MAX_MDIO_FREQ * 2), then we need to add - * 1 to the clock divisor or we will surely be above 2.5 MHz */ - if (host_clock % (MAX_MDIO_FREQ * 2)) - clk_div++; - - printk(KERN_DEBUG "%s(): Setting MDIO clock divisor to %u based " - "on %u Hz host clock.\n", __func__, clk_div, host_clock); - - of_node_put(np1); -issue: - axienet_iow(lp, XAE_MDIO_MC_OFFSET, - (((u32) clk_div) | XAE_MDIO_MC_MDIOEN_MASK)); + axienet_iow(lp, XAE_MDIO_MC_OFFSET, (((u32)clk_div) | + XAE_MDIO_MC_MDIOEN_MASK)); ret = axienet_mdio_wait_until_ready(lp); if (ret < 0) @@ -203,8 +211,7 @@ issue: if (!bus) return -ENOMEM; - np1 = of_get_parent(lp->phy_node); - of_address_to_resource(np1, 0, &res); + of_address_to_resource(npp, 0, &res); snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", (unsigned long long) res.start); @@ -233,7 +240,6 @@ issue: void axienet_mdio_teardown(struct axienet_local *lp) { mdiobus_unregister(lp->mii_bus); - kfree(lp->mii_bus->irq); mdiobus_free(lp->mii_bus); lp->mii_bus = NULL; } diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index 74234a5..a7c02f4 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1172,7 +1173,7 @@ static int xemaclite_of_probe(struct platform_device *ofdev) if (mac_address) /* Set the MAC address. */ - memcpy(ndev->dev_addr, mac_address, ETH_ALEN); + memcpy(ndev->dev_addr, mac_address, 6); else dev_warn(dev, "No MAC address found\n"); diff --git a/drivers/net/ethernet/xilinx/xilinx_emacps.c b/drivers/net/ethernet/xilinx/xilinx_emacps.c new file mode 100644 index 0000000..325e5ea --- /dev/null +++ b/drivers/net/ethernet/xilinx/xilinx_emacps.c @@ -0,0 +1,2922 @@ +/* + * Xilinx Ethernet: Linux driver for Ethernet. + * + * Author: Xilinx, Inc. + * + * 2010 (c) Xilinx, Inc. This file is licensed uner the terms of the GNU + * General Public License version 2. This program is licensed "as is" + * without any warranty of any kind, whether express or implied. + * + * This is a driver for xilinx processor sub-system (ps) ethernet device. + * This driver is mainly used in Linux 2.6.30 and above and it does _not_ + * support Linux 2.4 kernel due to certain new features (e.g. NAPI) is + * introduced in this driver. + * + * TODO: + * 1. JUMBO frame is not enabled per EPs spec. Please update it if this + * support is added in and set MAX_MTU to 9000. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/************************** Constant Definitions *****************************/ + +/* Must be shorter than length of ethtool_drvinfo.driver field to fit */ +#define DRIVER_NAME "xemacps" +#define DRIVER_DESCRIPTION "Xilinx Tri-Mode Ethernet MAC driver" +#define DRIVER_VERSION "1.00a" + +/* Transmission timeout is 3 seconds. */ +#define TX_TIMEOUT (3*HZ) + +/* for RX skb IP header word-aligned */ +#define RX_IP_ALIGN_OFFSET 2 + +/* DMA buffer descriptors must be aligned on a 4-byte boundary. */ +#define ALIGNMENT_BD 8 + +/* Maximum value for hash bits. 2**6 */ +#define XEMACPS_MAX_HASH_BITS 64 + +/* MDC clock division + * currently supporting 8, 16, 32, 48, 64, 96, 128, 224. + */ +enum { MDC_DIV_8 = 0, MDC_DIV_16, MDC_DIV_32, MDC_DIV_48, +MDC_DIV_64, MDC_DIV_96, MDC_DIV_128, MDC_DIV_224 }; + +/* Specify the receive buffer size in bytes, 64, 128, 192, 10240 */ +#define XEMACPS_RX_BUF_SIZE 1536 + +/* Number of receive buffer bytes as a unit, this is HW setup */ +#define XEMACPS_RX_BUF_UNIT 64 + +/* Default SEND and RECV buffer descriptors (BD) numbers. + * BD Space needed is (XEMACPS_SEND_BD_CNT+XEMACPS_RECV_BD_CNT)*8 + */ +#undef DEBUG +#define DEBUG + +#define XEMACPS_SEND_BD_CNT 256 +#define XEMACPS_RECV_BD_CNT 256 + +#define XEMACPS_NAPI_WEIGHT 64 + +/* Register offset definitions. Unless otherwise noted, register access is + * 32 bit. Names are self explained here. + */ +#define XEMACPS_NWCTRL_OFFSET 0x00000000 /* Network Control reg */ +#define XEMACPS_NWCFG_OFFSET 0x00000004 /* Network Config reg */ +#define XEMACPS_NWSR_OFFSET 0x00000008 /* Network Status reg */ +#define XEMACPS_USERIO_OFFSET 0x0000000C /* User IO reg */ +#define XEMACPS_DMACR_OFFSET 0x00000010 /* DMA Control reg */ +#define XEMACPS_TXSR_OFFSET 0x00000014 /* TX Status reg */ +#define XEMACPS_RXQBASE_OFFSET 0x00000018 /* RX Q Base address reg */ +#define XEMACPS_TXQBASE_OFFSET 0x0000001C /* TX Q Base address reg */ +#define XEMACPS_RXSR_OFFSET 0x00000020 /* RX Status reg */ +#define XEMACPS_ISR_OFFSET 0x00000024 /* Interrupt Status reg */ +#define XEMACPS_IER_OFFSET 0x00000028 /* Interrupt Enable reg */ +#define XEMACPS_IDR_OFFSET 0x0000002C /* Interrupt Disable reg */ +#define XEMACPS_IMR_OFFSET 0x00000030 /* Interrupt Mask reg */ +#define XEMACPS_PHYMNTNC_OFFSET 0x00000034 /* Phy Maintaince reg */ +#define XEMACPS_RXPAUSE_OFFSET 0x00000038 /* RX Pause Time reg */ +#define XEMACPS_TXPAUSE_OFFSET 0x0000003C /* TX Pause Time reg */ +#define XEMACPS_HASHL_OFFSET 0x00000080 /* Hash Low address reg */ +#define XEMACPS_HASHH_OFFSET 0x00000084 /* Hash High address reg */ +#define XEMACPS_LADDR1L_OFFSET 0x00000088 /* Specific1 addr low */ +#define XEMACPS_LADDR1H_OFFSET 0x0000008C /* Specific1 addr high */ +#define XEMACPS_LADDR2L_OFFSET 0x00000090 /* Specific2 addr low */ +#define XEMACPS_LADDR2H_OFFSET 0x00000094 /* Specific2 addr high */ +#define XEMACPS_LADDR3L_OFFSET 0x00000098 /* Specific3 addr low */ +#define XEMACPS_LADDR3H_OFFSET 0x0000009C /* Specific3 addr high */ +#define XEMACPS_LADDR4L_OFFSET 0x000000A0 /* Specific4 addr low */ +#define XEMACPS_LADDR4H_OFFSET 0x000000A4 /* Specific4 addr high */ +#define XEMACPS_MATCH1_OFFSET 0x000000A8 /* Type ID1 Match reg */ +#define XEMACPS_MATCH2_OFFSET 0x000000AC /* Type ID2 Match reg */ +#define XEMACPS_MATCH3_OFFSET 0x000000B0 /* Type ID3 Match reg */ +#define XEMACPS_MATCH4_OFFSET 0x000000B4 /* Type ID4 Match reg */ +#define XEMACPS_WOL_OFFSET 0x000000B8 /* Wake on LAN reg */ +#define XEMACPS_STRETCH_OFFSET 0x000000BC /* IPG Stretch reg */ +#define XEMACPS_SVLAN_OFFSET 0x000000C0 /* Stacked VLAN reg */ +#define XEMACPS_MODID_OFFSET 0x000000FC /* Module ID reg */ +#define XEMACPS_OCTTXL_OFFSET 0x00000100 /* Octects transmitted Low + reg */ +#define XEMACPS_OCTTXH_OFFSET 0x00000104 /* Octects transmitted High + reg */ +#define XEMACPS_TXCNT_OFFSET 0x00000108 /* Error-free Frmaes + transmitted counter */ +#define XEMACPS_TXBCCNT_OFFSET 0x0000010C /* Error-free Broadcast + Frames counter*/ +#define XEMACPS_TXMCCNT_OFFSET 0x00000110 /* Error-free Multicast + Frame counter */ +#define XEMACPS_TXPAUSECNT_OFFSET 0x00000114 /* Pause Frames Transmitted + Counter */ +#define XEMACPS_TX64CNT_OFFSET 0x00000118 /* Error-free 64 byte Frames + Transmitted counter */ +#define XEMACPS_TX65CNT_OFFSET 0x0000011C /* Error-free 65-127 byte + Frames Transmitted counter */ +#define XEMACPS_TX128CNT_OFFSET 0x00000120 /* Error-free 128-255 byte + Frames Transmitted counter */ +#define XEMACPS_TX256CNT_OFFSET 0x00000124 /* Error-free 256-511 byte + Frames transmitted counter */ +#define XEMACPS_TX512CNT_OFFSET 0x00000128 /* Error-free 512-1023 byte + Frames transmitted counter */ +#define XEMACPS_TX1024CNT_OFFSET 0x0000012C /* Error-free 1024-1518 byte + Frames transmitted counter */ +#define XEMACPS_TX1519CNT_OFFSET 0x00000130 /* Error-free larger than + 1519 byte Frames transmitted + Counter */ +#define XEMACPS_TXURUNCNT_OFFSET 0x00000134 /* TX under run error + Counter */ +#define XEMACPS_SNGLCOLLCNT_OFFSET 0x00000138 /* Single Collision Frame + Counter */ +#define XEMACPS_MULTICOLLCNT_OFFSET 0x0000013C /* Multiple Collision Frame + Counter */ +#define XEMACPS_EXCESSCOLLCNT_OFFSET 0x00000140 /* Excessive Collision Frame + Counter */ +#define XEMACPS_LATECOLLCNT_OFFSET 0x00000144 /* Late Collision Frame + Counter */ +#define XEMACPS_TXDEFERCNT_OFFSET 0x00000148 /* Deferred Transmission + Frame Counter */ +#define XEMACPS_CSENSECNT_OFFSET 0x0000014C /* Carrier Sense Error + Counter */ +#define XEMACPS_OCTRXL_OFFSET 0x00000150 /* Octects Received register + Low */ +#define XEMACPS_OCTRXH_OFFSET 0x00000154 /* Octects Received register + High */ +#define XEMACPS_RXCNT_OFFSET 0x00000158 /* Error-free Frames + Received Counter */ +#define XEMACPS_RXBROADCNT_OFFSET 0x0000015C /* Error-free Broadcast + Frames Received Counter */ +#define XEMACPS_RXMULTICNT_OFFSET 0x00000160 /* Error-free Multicast + Frames Received Counter */ +#define XEMACPS_RXPAUSECNT_OFFSET 0x00000164 /* Pause Frames + Received Counter */ +#define XEMACPS_RX64CNT_OFFSET 0x00000168 /* Error-free 64 byte Frames + Received Counter */ +#define XEMACPS_RX65CNT_OFFSET 0x0000016C /* Error-free 65-127 byte + Frames Received Counter */ +#define XEMACPS_RX128CNT_OFFSET 0x00000170 /* Error-free 128-255 byte + Frames Received Counter */ +#define XEMACPS_RX256CNT_OFFSET 0x00000174 /* Error-free 256-512 byte + Frames Received Counter */ +#define XEMACPS_RX512CNT_OFFSET 0x00000178 /* Error-free 512-1023 byte + Frames Received Counter */ +#define XEMACPS_RX1024CNT_OFFSET 0x0000017C /* Error-free 1024-1518 byte + Frames Received Counter */ +#define XEMACPS_RX1519CNT_OFFSET 0x00000180 /* Error-free 1519-max byte + Frames Received Counter */ +#define XEMACPS_RXUNDRCNT_OFFSET 0x00000184 /* Undersize Frames Received + Counter */ +#define XEMACPS_RXOVRCNT_OFFSET 0x00000188 /* Oversize Frames Received + Counter */ +#define XEMACPS_RXJABCNT_OFFSET 0x0000018C /* Jabbers Received + Counter */ +#define XEMACPS_RXFCSCNT_OFFSET 0x00000190 /* Frame Check Sequence + Error Counter */ +#define XEMACPS_RXLENGTHCNT_OFFSET 0x00000194 /* Length Field Error + Counter */ +#define XEMACPS_RXSYMBCNT_OFFSET 0x00000198 /* Symbol Error Counter */ +#define XEMACPS_RXALIGNCNT_OFFSET 0x0000019C /* Alignment Error + Counter */ +#define XEMACPS_RXRESERRCNT_OFFSET 0x000001A0 /* Receive Resource Error + Counter */ +#define XEMACPS_RXORCNT_OFFSET 0x000001A4 /* Receive Overrun */ +#define XEMACPS_RXIPCCNT_OFFSET 0x000001A8 /* IP header Checksum Error + Counter */ +#define XEMACPS_RXTCPCCNT_OFFSET 0x000001AC /* TCP Checksum Error + Counter */ +#define XEMACPS_RXUDPCCNT_OFFSET 0x000001B0 /* UDP Checksum Error + Counter */ + +#define XEMACPS_1588S_OFFSET 0x000001D0 /* 1588 Timer Seconds */ +#define XEMACPS_1588NS_OFFSET 0x000001D4 /* 1588 Timer Nanoseconds */ +#define XEMACPS_1588ADJ_OFFSET 0x000001D8 /* 1588 Timer Adjust */ +#define XEMACPS_1588INC_OFFSET 0x000001DC /* 1588 Timer Increment */ +#define XEMACPS_PTPETXS_OFFSET 0x000001E0 /* PTP Event Frame + Transmitted Seconds */ +#define XEMACPS_PTPETXNS_OFFSET 0x000001E4 /* PTP Event Frame + Transmitted Nanoseconds */ +#define XEMACPS_PTPERXS_OFFSET 0x000001E8 /* PTP Event Frame Received + Seconds */ +#define XEMACPS_PTPERXNS_OFFSET 0x000001EC /* PTP Event Frame Received + Nanoseconds */ +#define XEMACPS_PTPPTXS_OFFSET 0x000001E0 /* PTP Peer Frame + Transmitted Seconds */ +#define XEMACPS_PTPPTXNS_OFFSET 0x000001E4 /* PTP Peer Frame + Transmitted Nanoseconds */ +#define XEMACPS_PTPPRXS_OFFSET 0x000001E8 /* PTP Peer Frame Received + Seconds */ +#define XEMACPS_PTPPRXNS_OFFSET 0x000001EC /* PTP Peer Frame Received + Nanoseconds */ + +/* network control register bit definitions */ +#define XEMACPS_NWCTRL_FLUSH_DPRAM_MASK 0x00040000 +#define XEMACPS_NWCTRL_RXTSTAMP_MASK 0x00008000 /* RX Timestamp in CRC */ +#define XEMACPS_NWCTRL_ZEROPAUSETX_MASK 0x00001000 /* Transmit zero quantum + pause frame */ +#define XEMACPS_NWCTRL_PAUSETX_MASK 0x00000800 /* Transmit pause frame */ +#define XEMACPS_NWCTRL_HALTTX_MASK 0x00000400 /* Halt transmission + after current frame */ +#define XEMACPS_NWCTRL_STARTTX_MASK 0x00000200 /* Start tx (tx_go) */ + +#define XEMACPS_NWCTRL_STATWEN_MASK 0x00000080 /* Enable writing to + stat counters */ +#define XEMACPS_NWCTRL_STATINC_MASK 0x00000040 /* Increment statistic + registers */ +#define XEMACPS_NWCTRL_STATCLR_MASK 0x00000020 /* Clear statistic + registers */ +#define XEMACPS_NWCTRL_MDEN_MASK 0x00000010 /* Enable MDIO port */ +#define XEMACPS_NWCTRL_TXEN_MASK 0x00000008 /* Enable transmit */ +#define XEMACPS_NWCTRL_RXEN_MASK 0x00000004 /* Enable receive */ +#define XEMACPS_NWCTRL_LOOPEN_MASK 0x00000002 /* local loopback */ + +/* name network configuration register bit definitions */ +#define XEMACPS_NWCFG_BADPREAMBEN_MASK 0x20000000 /* disable rejection of + non-standard preamble */ +#define XEMACPS_NWCFG_IPDSTRETCH_MASK 0x10000000 /* enable transmit IPG */ +#define XEMACPS_NWCFG_FCSIGNORE_MASK 0x04000000 /* disable rejection of + FCS error */ +#define XEMACPS_NWCFG_HDRXEN_MASK 0x02000000 /* RX half duplex */ +#define XEMACPS_NWCFG_RXCHKSUMEN_MASK 0x01000000 /* enable RX checksum + offload */ +#define XEMACPS_NWCFG_PAUSECOPYDI_MASK 0x00800000 /* Do not copy pause + Frames to memory */ +#define XEMACPS_NWCFG_MDC_SHIFT_MASK 18 /* shift bits for MDC */ +#define XEMACPS_NWCFG_MDCCLKDIV_MASK 0x001C0000 /* MDC Mask PCLK divisor */ +#define XEMACPS_NWCFG_FCSREM_MASK 0x00020000 /* Discard FCS from + received frames */ +#define XEMACPS_NWCFG_LENGTHERRDSCRD_MASK 0x00010000 +/* RX length error discard */ +#define XEMACPS_NWCFG_RXOFFS_MASK 0x0000C000 /* RX buffer offset */ +#define XEMACPS_NWCFG_PAUSEEN_MASK 0x00002000 /* Enable pause TX */ +#define XEMACPS_NWCFG_RETRYTESTEN_MASK 0x00001000 /* Retry test */ +#define XEMACPS_NWCFG_1000_MASK 0x00000400 /* Gigbit mode */ +#define XEMACPS_NWCFG_EXTADDRMATCHEN_MASK 0x00000200 +/* External address match enable */ +#define XEMACPS_NWCFG_UCASTHASHEN_MASK 0x00000080 /* Receive unicast hash + frames */ +#define XEMACPS_NWCFG_MCASTHASHEN_MASK 0x00000040 /* Receive multicast hash + frames */ +#define XEMACPS_NWCFG_BCASTDI_MASK 0x00000020 /* Do not receive + broadcast frames */ +#define XEMACPS_NWCFG_COPYALLEN_MASK 0x00000010 /* Copy all frames */ + +#define XEMACPS_NWCFG_NVLANDISC_MASK 0x00000004 /* Receive only VLAN + frames */ +#define XEMACPS_NWCFG_FDEN_MASK 0x00000002 /* Full duplex */ +#define XEMACPS_NWCFG_100_MASK 0x00000001 /* 10 or 100 Mbs */ + +/* network status register bit definitaions */ +#define XEMACPS_NWSR_MDIOIDLE_MASK 0x00000004 /* PHY management idle */ +#define XEMACPS_NWSR_MDIO_MASK 0x00000002 /* Status of mdio_in */ + +/* MAC address register word 1 mask */ +#define XEMACPS_LADDR_MACH_MASK 0x0000FFFF /* Address bits[47:32] + bit[31:0] are in BOTTOM */ + +/* DMA control register bit definitions */ +#define XEMACPS_DMACR_RXBUF_MASK 0x00FF0000 /* Mask bit for RX buffer + size */ +#define XEMACPS_DMACR_RXBUF_SHIFT 16 /* Shift bit for RX buffer + size */ +#define XEMACPS_DMACR_TCPCKSUM_MASK 0x00000800 /* enable/disable TX + checksum offload */ +#define XEMACPS_DMACR_TXSIZE_MASK 0x00000400 /* TX buffer memory size */ +#define XEMACPS_DMACR_RXSIZE_MASK 0x00000300 /* RX buffer memory size */ +#define XEMACPS_DMACR_ENDIAN_MASK 0x00000080 /* Endian configuration */ +#define XEMACPS_DMACR_BLENGTH_MASK 0x0000001F /* Buffer burst length */ +#define XEMACPS_DMACR_BLENGTH_INCR16 0x00000010 /* Buffer burst length */ +#define XEMACPS_DMACR_BLENGTH_INCR8 0x00000008 /* Buffer burst length */ +#define XEMACPS_DMACR_BLENGTH_INCR4 0x00000004 /* Buffer burst length */ +#define XEMACPS_DMACR_BLENGTH_SINGLE 0x00000002 /* Buffer burst length */ + +/* transmit status register bit definitions */ +#define XEMACPS_TXSR_HRESPNOK_MASK 0x00000100 /* Transmit hresp not OK */ +#define XEMACPS_TXSR_COL1000_MASK 0x00000080 /* Collision Gbs mode */ +#define XEMACPS_TXSR_URUN_MASK 0x00000040 /* Transmit underrun */ +#define XEMACPS_TXSR_TXCOMPL_MASK 0x00000020 /* Transmit completed OK */ +#define XEMACPS_TXSR_BUFEXH_MASK 0x00000010 /* Transmit buffs exhausted + mid frame */ +#define XEMACPS_TXSR_TXGO_MASK 0x00000008 /* Status of go flag */ +#define XEMACPS_TXSR_RXOVR_MASK 0x00000004 /* Retry limit exceeded */ +#define XEMACPS_TXSR_COL100_MASK 0x00000002 /* Collision 10/100 mode */ +#define XEMACPS_TXSR_USEDREAD_MASK 0x00000001 /* TX buffer used bit set */ + +#define XEMACPS_TXSR_ERROR_MASK (XEMACPS_TXSR_HRESPNOK_MASK | \ + XEMACPS_TXSR_COL1000_MASK | \ + XEMACPS_TXSR_URUN_MASK | \ + XEMACPS_TXSR_BUFEXH_MASK | \ + XEMACPS_TXSR_RXOVR_MASK | \ + XEMACPS_TXSR_COL100_MASK | \ + XEMACPS_TXSR_USEDREAD_MASK) + +/* receive status register bit definitions */ +#define XEMACPS_RXSR_HRESPNOK_MASK 0x00000008 /* Receive hresp not OK */ +#define XEMACPS_RXSR_RXOVR_MASK 0x00000004 /* Receive overrun */ +#define XEMACPS_RXSR_FRAMERX_MASK 0x00000002 /* Frame received OK */ +#define XEMACPS_RXSR_BUFFNA_MASK 0x00000001 /* RX buffer used bit set */ + +#define XEMACPS_RXSR_ERROR_MASK (XEMACPS_RXSR_HRESPNOK_MASK | \ + XEMACPS_RXSR_RXOVR_MASK | \ + XEMACPS_RXSR_BUFFNA_MASK) + +/* interrupts bit definitions + * Bits definitions are same in XEMACPS_ISR_OFFSET, + * XEMACPS_IER_OFFSET, XEMACPS_IDR_OFFSET, and XEMACPS_IMR_OFFSET + */ +#define XEMACPS_IXR_PTPPSTX_MASK 0x02000000 /* PTP Psync transmitted */ +#define XEMACPS_IXR_PTPPDRTX_MASK 0x01000000 /* PTP Pdelay_req + transmitted */ +#define XEMACPS_IXR_PTPSTX_MASK 0x00800000 /* PTP Sync transmitted */ +#define XEMACPS_IXR_PTPDRTX_MASK 0x00400000 /* PTP Delay_req + transmitted */ +#define XEMACPS_IXR_PTPPSRX_MASK 0x00200000 /* PTP Psync received */ +#define XEMACPS_IXR_PTPPDRRX_MASK 0x00100000 /* PTP Pdelay_req + received */ +#define XEMACPS_IXR_PTPSRX_MASK 0x00080000 /* PTP Sync received */ +#define XEMACPS_IXR_PTPDRRX_MASK 0x00040000 /* PTP Delay_req received */ +#define XEMACPS_IXR_PAUSETX_MASK 0x00004000 /* Pause frame + transmitted */ +#define XEMACPS_IXR_PAUSEZERO_MASK 0x00002000 /* Pause time has reached + zero */ +#define XEMACPS_IXR_PAUSENZERO_MASK 0x00001000 /* Pause frame received */ +#define XEMACPS_IXR_HRESPNOK_MASK 0x00000800 /* hresp not ok */ +#define XEMACPS_IXR_RXOVR_MASK 0x00000400 /* Receive overrun + occurred */ +#define XEMACPS_IXR_TXCOMPL_MASK 0x00000080 /* Frame transmitted ok */ +#define XEMACPS_IXR_TXEXH_MASK 0x00000040 /* Transmit err occurred or + no buffers*/ +#define XEMACPS_IXR_RETRY_MASK 0x00000020 /* Retry limit exceeded */ +#define XEMACPS_IXR_URUN_MASK 0x00000010 /* Transmit underrun */ +#define XEMACPS_IXR_TXUSED_MASK 0x00000008 /* Tx buffer used bit read */ +#define XEMACPS_IXR_RXUSED_MASK 0x00000004 /* Rx buffer used bit read */ +#define XEMACPS_IXR_FRAMERX_MASK 0x00000002 /* Frame received ok */ +#define XEMACPS_IXR_MGMNT_MASK 0x00000001 /* PHY management complete */ +#define XEMACPS_IXR_ALL_MASK 0x03FC7FFE /* Everything except MDIO */ + +#define XEMACPS_IXR_TX_ERR_MASK (XEMACPS_IXR_TXEXH_MASK | \ + XEMACPS_IXR_RETRY_MASK | \ + XEMACPS_IXR_URUN_MASK | \ + XEMACPS_IXR_TXUSED_MASK) + +#define XEMACPS_IXR_RX_ERR_MASK (XEMACPS_IXR_HRESPNOK_MASK | \ + XEMACPS_IXR_RXUSED_MASK | \ + XEMACPS_IXR_RXOVR_MASK) +/* PHY Maintenance bit definitions */ +#define XEMACPS_PHYMNTNC_OP_MASK 0x40020000 /* operation mask bits */ +#define XEMACPS_PHYMNTNC_OP_R_MASK 0x20000000 /* read operation */ +#define XEMACPS_PHYMNTNC_OP_W_MASK 0x10000000 /* write operation */ +#define XEMACPS_PHYMNTNC_ADDR_MASK 0x0F800000 /* Address bits */ +#define XEMACPS_PHYMNTNC_REG_MASK 0x007C0000 /* register bits */ +#define XEMACPS_PHYMNTNC_DATA_MASK 0x0000FFFF /* data bits */ +#define XEMACPS_PHYMNTNC_PHYAD_SHIFT_MASK 23 /* Shift bits for PHYAD */ +#define XEMACPS_PHYMNTNC_PHREG_SHIFT_MASK 18 /* Shift bits for PHREG */ + +/* Wake on LAN bit definition */ +#define XEMACPS_WOL_MCAST_MASK 0x00080000 +#define XEMACPS_WOL_SPEREG1_MASK 0x00040000 +#define XEMACPS_WOL_ARP_MASK 0x00020000 +#define XEMACPS_WOL_MAGIC_MASK 0x00010000 +#define XEMACPS_WOL_ARP_ADDR_MASK 0x0000FFFF + +/* Buffer descriptor status words offset */ +#define XEMACPS_BD_ADDR_OFFSET 0x00000000 /**< word 0/addr of BDs */ +#define XEMACPS_BD_STAT_OFFSET 0x00000004 /**< word 1/status of BDs */ + +/* Transmit buffer descriptor status words bit positions. + * Transmit buffer descriptor consists of two 32-bit registers, + * the first - word0 contains a 32-bit address pointing to the location of + * the transmit data. + * The following register - word1, consists of various information to + * control transmit process. After transmit, this is updated with status + * information, whether the frame was transmitted OK or why it had failed. + */ +#define XEMACPS_TXBUF_USED_MASK 0x80000000 /* Used bit. */ +#define XEMACPS_TXBUF_WRAP_MASK 0x40000000 /* Wrap bit, last + descriptor */ +#define XEMACPS_TXBUF_RETRY_MASK 0x20000000 /* Retry limit exceeded */ +#define XEMACPS_TXBUF_EXH_MASK 0x08000000 /* Buffers exhausted */ +#define XEMACPS_TXBUF_LAC_MASK 0x04000000 /* Late collision. */ +#define XEMACPS_TXBUF_NOCRC_MASK 0x00010000 /* No CRC */ +#define XEMACPS_TXBUF_LAST_MASK 0x00008000 /* Last buffer */ +#define XEMACPS_TXBUF_LEN_MASK 0x00003FFF /* Mask for length field */ + +#define XEMACPS_TXBUF_ERR_MASK 0x3C000000 /* Mask for length field */ + +/* Receive buffer descriptor status words bit positions. + * Receive buffer descriptor consists of two 32-bit registers, + * the first - word0 contains a 32-bit word aligned address pointing to the + * address of the buffer. The lower two bits make up the wrap bit indicating + * the last descriptor and the ownership bit to indicate it has been used. + * The following register - word1, contains status information regarding why + * the frame was received (the filter match condition) as well as other + * useful info. + */ +#define XEMACPS_RXBUF_BCAST_MASK 0x80000000 /* Broadcast frame */ +#define XEMACPS_RXBUF_MULTIHASH_MASK 0x40000000 /* Multicast hashed frame */ +#define XEMACPS_RXBUF_UNIHASH_MASK 0x20000000 /* Unicast hashed frame */ +#define XEMACPS_RXBUF_EXH_MASK 0x08000000 /* buffer exhausted */ +#define XEMACPS_RXBUF_AMATCH_MASK 0x06000000 /* Specific address + matched */ +#define XEMACPS_RXBUF_IDFOUND_MASK 0x01000000 /* Type ID matched */ +#define XEMACPS_RXBUF_IDMATCH_MASK 0x00C00000 /* ID matched mask */ +#define XEMACPS_RXBUF_VLAN_MASK 0x00200000 /* VLAN tagged */ +#define XEMACPS_RXBUF_PRI_MASK 0x00100000 /* Priority tagged */ +#define XEMACPS_RXBUF_VPRI_MASK 0x000E0000 /* Vlan priority */ +#define XEMACPS_RXBUF_CFI_MASK 0x00010000 /* CFI frame */ +#define XEMACPS_RXBUF_EOF_MASK 0x00008000 /* End of frame. */ +#define XEMACPS_RXBUF_SOF_MASK 0x00004000 /* Start of frame. */ +#define XEMACPS_RXBUF_LEN_MASK 0x00003FFF /* Mask for length field */ + +#define XEMACPS_RXBUF_WRAP_MASK 0x00000002 /* Wrap bit, last BD */ +#define XEMACPS_RXBUF_NEW_MASK 0x00000001 /* Used bit.. */ +#define XEMACPS_RXBUF_ADD_MASK 0xFFFFFFFC /* Mask for address */ + +#define XEAMCPS_GEN_PURPOSE_TIMER_LOAD 100 /* timeout value is msecs */ + +#define XEMACPS_GMII2RGMII_FULLDPLX BMCR_FULLDPLX +#define XEMACPS_GMII2RGMII_SPEED1000 BMCR_SPEED1000 +#define XEMACPS_GMII2RGMII_SPEED100 BMCR_SPEED100 +#define XEMACPS_GMII2RGMII_REG_NUM 0x10 + +#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP +#define NS_PER_SEC 1000000000ULL /* Nanoseconds per + second */ +#endif + +#define xemacps_read(base, reg) \ + __raw_readl(((void __iomem *)(base)) + (reg)) +#define xemacps_write(base, reg, val) \ + __raw_writel((val), ((void __iomem *)(base)) + (reg)) + +struct ring_info { + struct sk_buff *skb; + dma_addr_t mapping; + size_t len; +}; + +/* DMA buffer descriptor structure. Each BD is two words */ +struct xemacps_bd { + u32 addr; + u32 ctrl; +}; + + +/* Our private device data. */ +struct net_local { + void __iomem *baseaddr; + struct clk *devclk; + struct clk *aperclk; + struct notifier_block clk_rate_change_nb; + + struct device_node *phy_node; + struct device_node *gmii2rgmii_phy_node; + struct ring_info *tx_skb; + struct ring_info *rx_skb; + + struct xemacps_bd *rx_bd; + struct xemacps_bd *tx_bd; + + dma_addr_t rx_bd_dma; /* physical address */ + dma_addr_t tx_bd_dma; /* physical address */ + + u32 tx_bd_ci; + u32 tx_bd_tail; + u32 rx_bd_ci; + + u32 tx_bd_freecnt; + + spinlock_t tx_lock; + spinlock_t rx_lock; + spinlock_t nwctrlreg_lock; + + struct platform_device *pdev; + struct net_device *ndev; /* this device */ + struct tasklet_struct tx_bdreclaim_tasklet; + struct workqueue_struct *txtimeout_handler_wq; + struct work_struct txtimeout_reinit; + + struct napi_struct napi; /* napi information for device */ + struct net_device_stats stats; /* Statistics for this device */ + + struct timer_list gen_purpose_timer; /* Used for stats update */ + + /* Manage internal timer for packet timestamping */ + struct cyclecounter cycles; + struct timecounter clock; + struct hwtstamp_config hwtstamp_config; + + struct mii_bus *mii_bus; + struct phy_device *phy_dev; + struct phy_device *gmii2rgmii_phy_dev; + phy_interface_t phy_interface; + unsigned int link; + unsigned int speed; + unsigned int duplex; + /* RX ip/tcp/udp checksum */ + unsigned ip_summed; + unsigned int enetnum; + unsigned int lastrxfrmscntr; +#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP + unsigned int ptpenetclk; +#endif +}; +#define to_net_local(_nb) container_of(_nb, struct net_local,\ + clk_rate_change_nb) + +static struct net_device_ops netdev_ops; + +/** + * xemacps_mdio_read - Read current value of phy register indicated by + * phyreg. + * @bus: mdio bus + * @mii_id: mii id + * @phyreg: phy register to be read + * + * @return: value read from specified phy register. + * + * note: This is for 802.3 clause 22 phys access. For 802.3 clause 45 phys + * access, set bit 30 to be 1. e.g. change XEMACPS_PHYMNTNC_OP_MASK to + * 0x00020000. + */ +static int xemacps_mdio_read(struct mii_bus *bus, int mii_id, int phyreg) +{ + struct net_local *lp = bus->priv; + u32 regval; + int value; + volatile u32 ipisr; + + regval = XEMACPS_PHYMNTNC_OP_MASK; + regval |= XEMACPS_PHYMNTNC_OP_R_MASK; + regval |= (mii_id << XEMACPS_PHYMNTNC_PHYAD_SHIFT_MASK); + regval |= (phyreg << XEMACPS_PHYMNTNC_PHREG_SHIFT_MASK); + + xemacps_write(lp->baseaddr, XEMACPS_PHYMNTNC_OFFSET, regval); + + /* wait for end of transfer */ + do { + cpu_relax(); + ipisr = xemacps_read(lp->baseaddr, XEMACPS_NWSR_OFFSET); + } while ((ipisr & XEMACPS_NWSR_MDIOIDLE_MASK) == 0); + + value = xemacps_read(lp->baseaddr, XEMACPS_PHYMNTNC_OFFSET) & + XEMACPS_PHYMNTNC_DATA_MASK; + + return value; +} + +/** + * xemacps_mdio_write - Write passed in value to phy register indicated + * by phyreg. + * @bus: mdio bus + * @mii_id: mii id + * @phyreg: phy register to be configured. + * @value: value to be written to phy register. + * return 0. This API requires to be int type or compile warning generated + * + * note: This is for 802.3 clause 22 phys access. For 802.3 clause 45 phys + * access, set bit 30 to be 1. e.g. change XEMACPS_PHYMNTNC_OP_MASK to + * 0x00020000. + */ +static int xemacps_mdio_write(struct mii_bus *bus, int mii_id, int phyreg, + u16 value) +{ + struct net_local *lp = bus->priv; + u32 regval; + volatile u32 ipisr; + + regval = XEMACPS_PHYMNTNC_OP_MASK; + regval |= XEMACPS_PHYMNTNC_OP_W_MASK; + regval |= (mii_id << XEMACPS_PHYMNTNC_PHYAD_SHIFT_MASK); + regval |= (phyreg << XEMACPS_PHYMNTNC_PHREG_SHIFT_MASK); + regval |= value; + + xemacps_write(lp->baseaddr, XEMACPS_PHYMNTNC_OFFSET, regval); + + /* wait for end of transfer */ + do { + cpu_relax(); + ipisr = xemacps_read(lp->baseaddr, XEMACPS_NWSR_OFFSET); + } while ((ipisr & XEMACPS_NWSR_MDIOIDLE_MASK) == 0); + + return 0; +} + + +/** + * xemacps_mdio_reset - mdio reset. It seems to be required per open + * source documentation phy.txt. But there is no reset in this device. + * Provide function API for now. + * @bus: mdio bus + **/ +static int xemacps_mdio_reset(struct mii_bus *bus) +{ + return 0; +} + +/** + * xemacps_set_freq() - Set a clock to a new frequency + * @clk Pointer to the clock to change + * @rate New frequency in Hz + * @dev Pointer to the struct device + */ +static void xemacps_set_freq(struct clk *clk, long rate, struct device *dev) +{ + rate = clk_round_rate(clk, rate); + if (rate < 0) + return; + + dev_info(dev, "Set clk to %ld Hz\n", rate); + if (clk_set_rate(clk, rate)) + dev_err(dev, "Setting new clock rate failed.\n"); +} + +/** + * xemacps_adjust_link - handles link status changes, such as speed, + * duplex, up/down, ... + * @ndev: network device + */ +static void xemacps_adjust_link(struct net_device *ndev) +{ + struct net_local *lp = netdev_priv(ndev); + struct phy_device *phydev = lp->phy_dev; + struct phy_device *gmii2rgmii_phydev = lp->gmii2rgmii_phy_dev; + int status_change = 0; + u32 regval; + u16 gmii2rgmii_reg = 0; + + if (phydev->link) { + if ((lp->speed != phydev->speed) || + (lp->duplex != phydev->duplex)) { + regval = xemacps_read(lp->baseaddr, + XEMACPS_NWCFG_OFFSET); + regval &= ~(XEMACPS_NWCFG_FDEN_MASK | + XEMACPS_NWCFG_1000_MASK | + XEMACPS_NWCFG_100_MASK); + + if (phydev->duplex) { + regval |= XEMACPS_NWCFG_FDEN_MASK; + gmii2rgmii_reg |= XEMACPS_GMII2RGMII_FULLDPLX; + } + + if (phydev->speed == SPEED_1000) { + regval |= XEMACPS_NWCFG_1000_MASK; + gmii2rgmii_reg |= XEMACPS_GMII2RGMII_SPEED1000; + xemacps_set_freq(lp->devclk, 125000000, + &lp->pdev->dev); + } + else if (phydev->speed == SPEED_100) { + regval |= XEMACPS_NWCFG_100_MASK; + gmii2rgmii_reg |= XEMACPS_GMII2RGMII_SPEED100; + xemacps_set_freq(lp->devclk, 25000000, + &lp->pdev->dev); + } + else if (phydev->speed == SPEED_10) { + xemacps_set_freq(lp->devclk, 2500000, + &lp->pdev->dev); + } + else { + dev_err(&lp->pdev->dev, + "%s: unknown PHY speed %d\n", + __func__, phydev->speed); + return; + } + + xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET, + regval); + + if (gmii2rgmii_phydev != NULL) { + xemacps_mdio_write(lp->mii_bus, + gmii2rgmii_phydev->addr, + XEMACPS_GMII2RGMII_REG_NUM, + gmii2rgmii_reg); + } + + lp->speed = phydev->speed; + lp->duplex = phydev->duplex; + status_change = 1; + } + } + + if (phydev->link != lp->link) { + lp->link = phydev->link; + status_change = 1; + } + + if (status_change) { + if (phydev->link) + dev_info(&lp->pdev->dev, "link up (%d/%s)\n", + phydev->speed, + DUPLEX_FULL == phydev->duplex ? + "FULL" : "HALF"); + else + dev_info(&lp->pdev->dev, "link down\n"); + } +} + +static int xemacps_clk_notifier_cb(struct notifier_block *nb, unsigned long + event, void *data) +{ +/* + struct clk_notifier_data *ndata = data; + struct net_local *nl = to_net_local(nb); +*/ + + switch (event) { + case PRE_RATE_CHANGE: + /* if a rate change is announced we need to check whether we can + * maintain the current frequency by changing the clock + * dividers. + * I don't see how this can be done using the current fmwk!? + * For now we always allow the rate change. Otherwise we would + * even prevent ourself to change the rate. + */ + return NOTIFY_OK; + case POST_RATE_CHANGE: + /* not sure this will work. actually i'm sure it does not. this + * callback is not allowed to call back into COMMON_CLK, what + * adjust_link() does...*/ + /*xemacps_adjust_link(nl->ndev); would likely lock up kernel */ + return NOTIFY_OK; + case ABORT_RATE_CHANGE: + default: + return NOTIFY_DONE; + } +} + +/** + * xemacps_mii_probe - probe mii bus, find the right bus_id to register + * phy callback function. + * @ndev: network interface device structure + * return 0 on success, negative value if error + **/ +static int xemacps_mii_probe(struct net_device *ndev) +{ + struct net_local *lp = netdev_priv(ndev); + struct phy_device *phydev = NULL; + + if (lp->phy_node) { + phydev = of_phy_connect(lp->ndev, + lp->phy_node, + &xemacps_adjust_link, + 0, + lp->phy_interface); + } + if (!phydev) { + dev_err(&lp->pdev->dev, "%s: no PHY found\n", ndev->name); + return -1; + } + + dev_dbg(&lp->pdev->dev, + "GEM: phydev %p, phydev->phy_id 0x%x, phydev->addr 0x%x\n", + phydev, phydev->phy_id, phydev->addr); + + phydev->supported &= (PHY_GBIT_FEATURES | SUPPORTED_Pause | + SUPPORTED_Asym_Pause); + phydev->advertising = phydev->supported; + + lp->link = 0; + lp->speed = 0; + lp->duplex = -1; + lp->phy_dev = phydev; + + phy_start(lp->phy_dev); + + dev_dbg(&lp->pdev->dev, "phy_addr 0x%x, phy_id 0x%08x\n", + lp->phy_dev->addr, lp->phy_dev->phy_id); + + dev_dbg(&lp->pdev->dev, "attach [%s] phy driver\n", + lp->phy_dev->drv->name); + + if (lp->gmii2rgmii_phy_node) { + phydev = of_phy_connect(lp->ndev, + lp->gmii2rgmii_phy_node, + NULL, + 0, 0); + if (!phydev) { + dev_err(&lp->pdev->dev, "%s: no gmii to rgmii converter found\n", + ndev->name); + return -1; + } + lp->gmii2rgmii_phy_dev = phydev; + } else + lp->gmii2rgmii_phy_dev = NULL; + + return 0; +} + +/** + * xemacps_mii_init - Initialize and register mii bus to network device + * @lp: local device instance pointer + * return 0 on success, negative value if error + **/ +static int xemacps_mii_init(struct net_local *lp) +{ + int rc = -ENXIO, i; + struct resource res; + struct device_node *np = of_get_parent(lp->phy_node); + struct device_node *npp; + + lp->mii_bus = mdiobus_alloc(); + if (lp->mii_bus == NULL) { + rc = -ENOMEM; + goto err_out; + } + + lp->mii_bus->name = "XEMACPS mii bus"; + lp->mii_bus->read = &xemacps_mdio_read; + lp->mii_bus->write = &xemacps_mdio_write; + lp->mii_bus->reset = &xemacps_mdio_reset; + lp->mii_bus->priv = lp; + lp->mii_bus->parent = &lp->ndev->dev; + + lp->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); + if (!lp->mii_bus->irq) { + rc = -ENOMEM; + goto err_out_free_mdiobus; + } + + for (i = 0; i < PHY_MAX_ADDR; i++) + lp->mii_bus->irq[i] = PHY_POLL; + npp = of_get_parent(np); + of_address_to_resource(npp, 0, &res); + snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "%.8llx", + (unsigned long long)res.start); + if (of_mdiobus_register(lp->mii_bus, np)) + goto err_out_free_mdio_irq; + + return 0; + +err_out_free_mdio_irq: + kfree(lp->mii_bus->irq); +err_out_free_mdiobus: + mdiobus_free(lp->mii_bus); +err_out: + return rc; +} + +/** + * xemacps_update_hdaddr - Update device's MAC address when configured + * MAC address is not valid, reconfigure with a good one. + * @lp: local device instance pointer + **/ +static void xemacps_update_hwaddr(struct net_local *lp) +{ + u32 regvall; + u16 regvalh; + u8 addr[6]; + + regvall = xemacps_read(lp->baseaddr, XEMACPS_LADDR1L_OFFSET); + regvalh = xemacps_read(lp->baseaddr, XEMACPS_LADDR1H_OFFSET); + addr[0] = regvall & 0xFF; + addr[1] = (regvall >> 8) & 0xFF; + addr[2] = (regvall >> 16) & 0xFF; + addr[3] = (regvall >> 24) & 0xFF; + addr[4] = regvalh & 0xFF; + addr[5] = (regvalh >> 8) & 0xFF; + + if (is_valid_ether_addr(addr)) { + memcpy(lp->ndev->dev_addr, addr, sizeof(addr)); + } else { + dev_info(&lp->pdev->dev, "invalid address, use assigned\n"); + random_ether_addr(lp->ndev->dev_addr); + dev_info(&lp->pdev->dev, + "MAC updated %02x:%02x:%02x:%02x:%02x:%02x\n", + lp->ndev->dev_addr[0], lp->ndev->dev_addr[1], + lp->ndev->dev_addr[2], lp->ndev->dev_addr[3], + lp->ndev->dev_addr[4], lp->ndev->dev_addr[5]); + } +} + +/** + * xemacps_set_hwaddr - Set device's MAC address from ndev->dev_addr + * @lp: local device instance pointer + **/ +static void xemacps_set_hwaddr(struct net_local *lp) +{ + u32 regvall = 0; + u16 regvalh = 0; +#ifdef __LITTLE_ENDIAN + regvall = cpu_to_le32(*((u32 *)lp->ndev->dev_addr)); + regvalh = cpu_to_le16(*((u16 *)(lp->ndev->dev_addr + 4))); +#endif +#ifdef __BIG_ENDIAN + regvall = cpu_to_be32(*((u32 *)lp->ndev->dev_addr)); + regvalh = cpu_to_be16(*((u16 *)(lp->ndev->dev_addr + 4))); +#endif + /* LADDRXH has to be wriiten latter than LADDRXL to enable + * this address even if these 16 bits are zeros. */ + xemacps_write(lp->baseaddr, XEMACPS_LADDR1L_OFFSET, regvall); + xemacps_write(lp->baseaddr, XEMACPS_LADDR1H_OFFSET, regvalh); +#ifdef DEBUG + regvall = xemacps_read(lp->baseaddr, XEMACPS_LADDR1L_OFFSET); + regvalh = xemacps_read(lp->baseaddr, XEMACPS_LADDR1H_OFFSET); + dev_dbg(&lp->pdev->dev, + "MAC 0x%08x, 0x%08x, %02x:%02x:%02x:%02x:%02x:%02x\n", + regvall, regvalh, + (regvall & 0xff), ((regvall >> 8) & 0xff), + ((regvall >> 16) & 0xff), (regvall >> 24), + (regvalh & 0xff), (regvalh >> 8)); +#endif +} + +/* + * xemacps_reset_hw - Helper function to reset the underlying hardware. + * This is called when we get into such deep trouble that we don't know + * how to handle otherwise. + * @lp: local device instance pointer + */ +static void xemacps_reset_hw(struct net_local *lp) +{ + u32 regisr; + /* make sure we have the buffer for ourselves */ + wmb(); + + /* Have a clean start */ + xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, 0); + + /* Clear statistic counters */ + xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, + XEMACPS_NWCTRL_STATCLR_MASK); + + /* Clear TX and RX status */ + xemacps_write(lp->baseaddr, XEMACPS_TXSR_OFFSET, ~0UL); + xemacps_write(lp->baseaddr, XEMACPS_RXSR_OFFSET, ~0UL); + + /* Disable all interrupts */ + xemacps_write(lp->baseaddr, XEMACPS_IDR_OFFSET, ~0UL); + synchronize_irq(lp->ndev->irq); + regisr = xemacps_read(lp->baseaddr, XEMACPS_ISR_OFFSET); + xemacps_write(lp->baseaddr, XEMACPS_ISR_OFFSET, regisr); +} + +#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP + +/** + * xemacps_get_hwticks - get the current value of the GEM internal timer + * @lp: local device instance pointer + * return: nothing + **/ +static inline void +xemacps_get_hwticks(struct net_local *lp, u64 *sec, u64 *nsec) +{ + do { + *nsec = xemacps_read(lp->baseaddr, XEMACPS_1588NS_OFFSET); + *sec = xemacps_read(lp->baseaddr, XEMACPS_1588S_OFFSET); + } while (*nsec > xemacps_read(lp->baseaddr, XEMACPS_1588NS_OFFSET)); +} + +/** + * xemacps_read_clock - read raw cycle counter (to be used by time counter) + */ +static cycle_t xemacps_read_clock(const struct cyclecounter *tc) +{ + struct net_local *lp = + container_of(tc, struct net_local, cycles); + u64 stamp; + u64 sec, nsec; + + xemacps_get_hwticks(lp, &sec, &nsec); + stamp = (sec << 32) | nsec; + + return stamp; +} + + +/** + * xemacps_systim_to_hwtstamp - convert system time value to hw timestamp + * @adapter: board private structure + * @shhwtstamps: timestamp structure to update + * @regval: unsigned 64bit system time value. + * + * We need to convert the system time value stored in the RX/TXSTMP registers + * into a hwtstamp which can be used by the upper level timestamping functions + */ +static void xemacps_systim_to_hwtstamp(struct net_local *lp, + struct skb_shared_hwtstamps *shhwtstamps, + u64 regval) +{ + u64 ns; + + ns = timecounter_cyc2time(&lp->clock, regval); + timecompare_update(&lp->compare, ns); + memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); + shhwtstamps->hwtstamp = ns_to_ktime(ns); + shhwtstamps->syststamp = timecompare_transform(&lp->compare, ns); +} + +static void +xemacps_rx_hwtstamp(struct net_local *lp, + struct sk_buff *skb, unsigned msg_type) +{ + u64 time64, sec, nsec; + + if (!msg_type) { + /* PTP Event Frame packets */ + sec = xemacps_read(lp->baseaddr, XEMACPS_PTPERXS_OFFSET); + nsec = xemacps_read(lp->baseaddr, XEMACPS_PTPERXNS_OFFSET); + } else { + /* PTP Peer Event Frame packets */ + sec = xemacps_read(lp->baseaddr, XEMACPS_PTPPRXS_OFFSET); + nsec = xemacps_read(lp->baseaddr, XEMACPS_PTPPRXNS_OFFSET); + } + time64 = (sec << 32) | nsec; + xemacps_systim_to_hwtstamp(lp, skb_hwtstamps(skb), time64); +} + +static void +xemacps_tx_hwtstamp(struct net_local *lp, + struct sk_buff *skb, unsigned msg_type) +{ + u64 time64, sec, nsec; + + if (!msg_type) { + /* PTP Event Frame packets */ + sec = xemacps_read(lp->baseaddr, XEMACPS_PTPETXS_OFFSET); + nsec = xemacps_read(lp->baseaddr, XEMACPS_PTPETXNS_OFFSET); + } else { + /* PTP Peer Event Frame packets */ + sec = xemacps_read(lp->baseaddr, XEMACPS_PTPPTXS_OFFSET); + nsec = xemacps_read(lp->baseaddr, XEMACPS_PTPPTXNS_OFFSET); + } + + time64 = (sec << 32) | nsec; + xemacps_systim_to_hwtstamp(lp, skb_hwtstamps(skb), time64); + skb_tstamp_tx(skb, skb_hwtstamps(skb)); +} + +#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */ + +/** + * xemacps_rx - process received packets when napi called + * @lp: local device instance pointer + * @budget: NAPI budget + * return: number of BDs processed + **/ +static int xemacps_rx(struct net_local *lp, int budget) +{ + struct xemacps_bd *cur_p; + u32 len; + struct sk_buff *skb; + struct sk_buff *new_skb; + u32 new_skb_baddr; + unsigned int numbdfree = 0; + u32 size = 0; + u32 packets = 0; + u32 regval; + + cur_p = &lp->rx_bd[lp->rx_bd_ci]; + regval = cur_p->addr; + rmb(); + while (numbdfree < budget) { + if (!(regval & XEMACPS_RXBUF_NEW_MASK)) + break; + + new_skb = netdev_alloc_skb(lp->ndev, XEMACPS_RX_BUF_SIZE); + if (new_skb == NULL) { + dev_err(&lp->ndev->dev, "no memory for new sk_buff\n"); + break; + } + /* Get dma handle of skb->data */ + new_skb_baddr = (u32) dma_map_single(lp->ndev->dev.parent, + new_skb->data, + XEMACPS_RX_BUF_SIZE, + DMA_FROM_DEVICE); + + /* the packet length */ + len = cur_p->ctrl & XEMACPS_RXBUF_LEN_MASK; + rmb(); + skb = lp->rx_skb[lp->rx_bd_ci].skb; + dma_unmap_single(lp->ndev->dev.parent, + lp->rx_skb[lp->rx_bd_ci].mapping, + lp->rx_skb[lp->rx_bd_ci].len, + DMA_FROM_DEVICE); + + /* setup received skb and send it upstream */ + skb_put(skb, len); /* Tell the skb how much data we got. */ + skb->protocol = eth_type_trans(skb, lp->ndev); + + skb->ip_summed = lp->ip_summed; + +#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP + if ((lp->hwtstamp_config.rx_filter == HWTSTAMP_FILTER_ALL) && + (ntohs(skb->protocol) == 0x800)) { + unsigned ip_proto, dest_port, msg_type; + + /* While the GEM can timestamp PTP packets, it does + * not mark the RX descriptor to identify them. This + * is entirely the wrong place to be parsing UDP + * headers, but some minimal effort must be made. + * NOTE: the below parsing of ip_proto and dest_port + * depend on the use of Ethernet_II encapsulation, + * IPv4 without any options. + */ + ip_proto = *((u8 *)skb->mac_header + 14 + 9); + dest_port = ntohs(*(((u16 *)skb->mac_header) + + ((14 + 20 + 2)/2))); + msg_type = *((u8 *)skb->mac_header + 42); + if ((ip_proto == IPPROTO_UDP) && + (dest_port == 0x13F)) { + /* Timestamp this packet */ + xemacps_rx_hwtstamp(lp, skb, msg_type & 0x2); + } + } +#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */ + size += len; + packets++; + netif_receive_skb(skb); + + cur_p->addr = (cur_p->addr & ~XEMACPS_RXBUF_ADD_MASK) + | (new_skb_baddr); + lp->rx_skb[lp->rx_bd_ci].skb = new_skb; + lp->rx_skb[lp->rx_bd_ci].mapping = new_skb_baddr; + lp->rx_skb[lp->rx_bd_ci].len = XEMACPS_RX_BUF_SIZE; + + cur_p->ctrl = 0; + cur_p->addr &= (~XEMACPS_RXBUF_NEW_MASK); + wmb(); + + lp->rx_bd_ci++; + lp->rx_bd_ci = lp->rx_bd_ci % XEMACPS_RECV_BD_CNT; + cur_p = &lp->rx_bd[lp->rx_bd_ci]; + regval = cur_p->addr; + rmb(); + numbdfree++; + } + wmb(); + lp->stats.rx_packets += packets; + lp->stats.rx_bytes += size; + return numbdfree; +} + +/** + * xemacps_rx_poll - NAPI poll routine + * napi: pointer to napi struct + * budget: + **/ +static int xemacps_rx_poll(struct napi_struct *napi, int budget) +{ + struct net_local *lp = container_of(napi, struct net_local, napi); + int work_done = 0; + u32 regval; + + spin_lock(&lp->rx_lock); + while (1) { + regval = xemacps_read(lp->baseaddr, XEMACPS_RXSR_OFFSET); + xemacps_write(lp->baseaddr, XEMACPS_RXSR_OFFSET, regval); + if (regval & XEMACPS_RXSR_HRESPNOK_MASK) + dev_err(&lp->pdev->dev, "RX error 0x%x\n", regval); + + work_done += xemacps_rx(lp, budget - work_done); + if (work_done >= budget) + break; + + napi_complete(napi); + /* We disabled RX interrupts in interrupt service + * routine, now it is time to enable it back. + */ + xemacps_write(lp->baseaddr, + XEMACPS_IER_OFFSET, XEMACPS_IXR_FRAMERX_MASK); + + /* If a packet has come in between the last check of the BD + * list and unmasking the interrupts, we may have missed the + * interrupt, so reschedule here. + */ + if ((lp->rx_bd[lp->rx_bd_ci].addr & XEMACPS_RXBUF_NEW_MASK) + && napi_reschedule(napi)) { + xemacps_write(lp->baseaddr, + XEMACPS_IDR_OFFSET, XEMACPS_IXR_FRAMERX_MASK); + continue; + } + break; + } + spin_unlock(&lp->rx_lock); + return work_done; +} + +/** + * xemacps_tx_poll - tx bd reclaim tasklet handler + * @data: pointer to network interface device structure + **/ +static void xemacps_tx_poll(unsigned long data) +{ + struct net_device *ndev = (struct net_device *)data; + struct net_local *lp = netdev_priv(ndev); + u32 regval; + u32 len = 0; + unsigned int bdcount = 0; + unsigned int bdpartialcount = 0; + unsigned int sop = 0; + struct xemacps_bd *cur_p; + u32 cur_i; + u32 numbdstofree; + u32 numbdsinhw; + struct ring_info *rp; + struct sk_buff *skb; + unsigned long flags; + + spin_lock(&lp->tx_lock); + regval = xemacps_read(lp->baseaddr, XEMACPS_TXSR_OFFSET); + xemacps_write(lp->baseaddr, XEMACPS_TXSR_OFFSET, regval); + dev_dbg(&lp->pdev->dev, "TX status 0x%x\n", regval); + if (regval & (XEMACPS_TXSR_HRESPNOK_MASK | XEMACPS_TXSR_BUFEXH_MASK)) + dev_err(&lp->pdev->dev, "TX error 0x%x\n", regval); + + cur_i = lp->tx_bd_ci; + cur_p = &lp->tx_bd[cur_i]; + numbdsinhw = XEMACPS_SEND_BD_CNT - lp->tx_bd_freecnt; + while (bdcount < numbdsinhw) { + if (sop == 0) { + if (cur_p->ctrl & XEMACPS_TXBUF_USED_MASK) + sop = 1; + else + break; + } + + bdcount++; + bdpartialcount++; + + /* hardware has processed this BD so check the "last" bit. + * If it is clear, then there are more BDs for the current + * packet. Keep a count of these partial packet BDs. + */ + if (cur_p->ctrl & XEMACPS_TXBUF_LAST_MASK) { + sop = 0; + bdpartialcount = 0; + } + + cur_i++; + cur_i = cur_i % XEMACPS_SEND_BD_CNT; + cur_p = &lp->tx_bd[cur_i]; + } + numbdstofree = bdcount - bdpartialcount; + lp->tx_bd_freecnt += numbdstofree; + numbdsinhw -= numbdstofree; + if (!numbdstofree) + goto tx_poll_out; + + cur_p = &lp->tx_bd[lp->tx_bd_ci]; + while (numbdstofree) { + rp = &lp->tx_skb[lp->tx_bd_ci]; + skb = rp->skb; + + len += (cur_p->ctrl & XEMACPS_TXBUF_LEN_MASK); + +#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP + if ((lp->hwtstamp_config.tx_type == HWTSTAMP_TX_ON) && + (ntohs(skb->protocol) == 0x800)) { + unsigned ip_proto, dest_port, msg_type; + + skb_reset_mac_header(skb); + + ip_proto = *((u8 *)skb->mac_header + 14 + 9); + dest_port = ntohs(*(((u16 *)skb->mac_header) + + ((14 + 20 + 2)/2))); + msg_type = *((u8 *)skb->mac_header + 42); + if ((ip_proto == IPPROTO_UDP) && + (dest_port == 0x13F)) { + /* Timestamp this packet */ + xemacps_tx_hwtstamp(lp, skb, msg_type & 0x2); + } + } +#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */ + + dma_unmap_single(&lp->pdev->dev, rp->mapping, rp->len, + DMA_TO_DEVICE); + rp->skb = NULL; + dev_kfree_skb(skb); + /* log tx completed packets and bytes, errors logs + * are in other error counters. + */ + if (cur_p->ctrl & XEMACPS_TXBUF_LAST_MASK) { + lp->stats.tx_packets++; + lp->stats.tx_bytes += len; + len = 0; + } + + /* Set used bit, preserve wrap bit; clear everything else. */ + cur_p->ctrl |= XEMACPS_TXBUF_USED_MASK; + cur_p->ctrl &= (XEMACPS_TXBUF_USED_MASK | + XEMACPS_TXBUF_WRAP_MASK); + + lp->tx_bd_ci++; + lp->tx_bd_ci = lp->tx_bd_ci % XEMACPS_SEND_BD_CNT; + cur_p = &lp->tx_bd[lp->tx_bd_ci]; + numbdstofree--; + } + wmb(); + + if (numbdsinhw) { + spin_lock_irqsave(&lp->nwctrlreg_lock, flags); + regval = xemacps_read(lp->baseaddr, XEMACPS_NWCTRL_OFFSET); + regval |= XEMACPS_NWCTRL_STARTTX_MASK; + xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, regval); + spin_unlock_irqrestore(&lp->nwctrlreg_lock, flags); + } + + netif_wake_queue(ndev); + +tx_poll_out: + spin_unlock(&lp->tx_lock); +} + +/** + * xemacps_interrupt - interrupt main service routine + * @irq: interrupt number + * @dev_id: pointer to a network device structure + * return IRQ_HANDLED or IRQ_NONE + **/ +static irqreturn_t xemacps_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = dev_id; + struct net_local *lp = netdev_priv(ndev); + u32 regisr; + u32 regctrl; + + regisr = xemacps_read(lp->baseaddr, XEMACPS_ISR_OFFSET); + if (unlikely(!regisr)) + return IRQ_NONE; + + xemacps_write(lp->baseaddr, XEMACPS_ISR_OFFSET, regisr); + + while (regisr) { + if (regisr & (XEMACPS_IXR_TXCOMPL_MASK | + XEMACPS_IXR_TX_ERR_MASK)) { + tasklet_schedule(&lp->tx_bdreclaim_tasklet); + } + + if (regisr & XEMACPS_IXR_RXUSED_MASK) { + spin_lock(&lp->nwctrlreg_lock); + regctrl = xemacps_read(lp->baseaddr, + XEMACPS_NWCTRL_OFFSET); + regctrl |= XEMACPS_NWCTRL_FLUSH_DPRAM_MASK; + xemacps_write(lp->baseaddr, + XEMACPS_NWCTRL_OFFSET, regctrl); + spin_unlock(&lp->nwctrlreg_lock); + } + + if (regisr & XEMACPS_IXR_FRAMERX_MASK) { + xemacps_write(lp->baseaddr, + XEMACPS_IDR_OFFSET, XEMACPS_IXR_FRAMERX_MASK); + napi_schedule(&lp->napi); + } + regisr = xemacps_read(lp->baseaddr, XEMACPS_ISR_OFFSET); + xemacps_write(lp->baseaddr, XEMACPS_ISR_OFFSET, regisr); + } + + return IRQ_HANDLED; +} + +/* + * Free all packets presently in the descriptor rings. + */ +static void xemacps_clean_rings(struct net_local *lp) +{ + int i; + + for (i = 0; i < XEMACPS_RECV_BD_CNT; i++) { + if (lp->rx_skb && lp->rx_skb[i].skb) { + dma_unmap_single(lp->ndev->dev.parent, + lp->rx_skb[i].mapping, + lp->rx_skb[i].len, + DMA_FROM_DEVICE); + + dev_kfree_skb(lp->rx_skb[i].skb); + lp->rx_skb[i].skb = NULL; + lp->rx_skb[i].mapping = 0; + } + } + + for (i = 0; i < XEMACPS_SEND_BD_CNT; i++) { + if (lp->tx_skb && lp->tx_skb[i].skb) { + dma_unmap_single(lp->ndev->dev.parent, + lp->tx_skb[i].mapping, + lp->tx_skb[i].len, + DMA_TO_DEVICE); + + dev_kfree_skb(lp->tx_skb[i].skb); + lp->tx_skb[i].skb = NULL; + lp->tx_skb[i].mapping = 0; + } + } +} + +/** + * xemacps_descriptor_free - Free allocated TX and RX BDs + * @lp: local device instance pointer + **/ +static void xemacps_descriptor_free(struct net_local *lp) +{ + int size; + + xemacps_clean_rings(lp); + + /* kfree(NULL) is safe, no need to check here */ + kfree(lp->tx_skb); + lp->tx_skb = NULL; + kfree(lp->rx_skb); + lp->rx_skb = NULL; + + size = XEMACPS_RECV_BD_CNT * sizeof(struct xemacps_bd); + if (lp->rx_bd) { + dma_free_coherent(&lp->pdev->dev, size, + lp->rx_bd, lp->rx_bd_dma); + lp->rx_bd = NULL; + } + + size = XEMACPS_SEND_BD_CNT * sizeof(struct xemacps_bd); + if (lp->tx_bd) { + dma_free_coherent(&lp->pdev->dev, size, + lp->tx_bd, lp->tx_bd_dma); + lp->tx_bd = NULL; + } +} + +/** + * xemacps_descriptor_init - Allocate both TX and RX BDs + * @lp: local device instance pointer + * return 0 on success, negative value if error + **/ +static int xemacps_descriptor_init(struct net_local *lp) +{ + int size; + struct sk_buff *new_skb; + u32 new_skb_baddr; + u32 i; + struct xemacps_bd *cur_p; + u32 regval; + + lp->tx_skb = NULL; + lp->rx_skb = NULL; + lp->rx_bd = NULL; + lp->tx_bd = NULL; + + /* Reset the indexes which are used for accessing the BDs */ + lp->tx_bd_ci = 0; + lp->tx_bd_tail = 0; + lp->rx_bd_ci = 0; + + size = XEMACPS_SEND_BD_CNT * sizeof(struct ring_info); + lp->tx_skb = kzalloc(size, GFP_KERNEL); + if (!lp->tx_skb) + goto err_out; + size = XEMACPS_RECV_BD_CNT * sizeof(struct ring_info); + lp->rx_skb = kzalloc(size, GFP_KERNEL); + if (!lp->rx_skb) + goto err_out; + + /* + * Set up RX buffer descriptors. + */ + + size = XEMACPS_RECV_BD_CNT * sizeof(struct xemacps_bd); + lp->rx_bd = dma_alloc_coherent(&lp->pdev->dev, size, + &lp->rx_bd_dma, GFP_KERNEL); + if (!lp->rx_bd) + goto err_out; + dev_dbg(&lp->pdev->dev, "RX ring %d bytes at 0x%x mapped %p\n", + size, lp->rx_bd_dma, lp->rx_bd); + + for (i = 0; i < XEMACPS_RECV_BD_CNT; i++) { + cur_p = &lp->rx_bd[i]; + + new_skb = netdev_alloc_skb(lp->ndev, XEMACPS_RX_BUF_SIZE); + if (new_skb == NULL) { + dev_err(&lp->ndev->dev, "alloc_skb error %d\n", i); + goto err_out; + } + + /* Get dma handle of skb->data */ + new_skb_baddr = (u32) dma_map_single(lp->ndev->dev.parent, + new_skb->data, + XEMACPS_RX_BUF_SIZE, + DMA_FROM_DEVICE); + + /* set wrap bit for last BD */ + regval = (new_skb_baddr & XEMACPS_RXBUF_ADD_MASK); + if (i == XEMACPS_RECV_BD_CNT - 1) + regval |= XEMACPS_RXBUF_WRAP_MASK; + cur_p->addr = regval; + cur_p->ctrl = 0; + wmb(); + + lp->rx_skb[i].skb = new_skb; + lp->rx_skb[i].mapping = new_skb_baddr; + lp->rx_skb[i].len = XEMACPS_RX_BUF_SIZE; + } + + /* + * Set up TX buffer descriptors. + */ + + size = XEMACPS_SEND_BD_CNT * sizeof(struct xemacps_bd); + lp->tx_bd = dma_alloc_coherent(&lp->pdev->dev, size, + &lp->tx_bd_dma, GFP_KERNEL); + if (!lp->tx_bd) + goto err_out; + dev_dbg(&lp->pdev->dev, "TX ring %d bytes at 0x%x mapped %p\n", + size, lp->tx_bd_dma, lp->tx_bd); + + for (i = 0; i < XEMACPS_SEND_BD_CNT; i++) { + cur_p = &lp->tx_bd[i]; + /* set wrap bit for last BD */ + cur_p->addr = 0; + regval = XEMACPS_TXBUF_USED_MASK; + if (i == XEMACPS_SEND_BD_CNT - 1) + regval |= XEMACPS_TXBUF_WRAP_MASK; + cur_p->ctrl = regval; + } + wmb(); + + lp->tx_bd_freecnt = XEMACPS_SEND_BD_CNT; + + dev_dbg(&lp->pdev->dev, + "lp->tx_bd %p lp->tx_bd_dma %p lp->tx_skb %p\n", + lp->tx_bd, (void *)lp->tx_bd_dma, lp->tx_skb); + dev_dbg(&lp->pdev->dev, + "lp->rx_bd %p lp->rx_bd_dma %p lp->rx_skb %p\n", + lp->rx_bd, (void *)lp->rx_bd_dma, lp->rx_skb); + + return 0; + +err_out: + xemacps_descriptor_free(lp); + return -ENOMEM; +} + +#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP +/* + * Initialize the GEM Time Stamp Unit + */ +static void xemacps_init_tsu(struct net_local *lp) +{ + + memset(&lp->cycles, 0, sizeof(lp->cycles)); + lp->cycles.read = xemacps_read_clock; + lp->cycles.mask = CLOCKSOURCE_MASK(64); + lp->cycles.mult = 1; + lp->cycles.shift = 0; + + /* Set registers so that rollover occurs soon to test this. */ + xemacps_write(lp->baseaddr, XEMACPS_1588NS_OFFSET, 0x00000000); + xemacps_write(lp->baseaddr, XEMACPS_1588S_OFFSET, 0xFF800000); + + /* program the timer increment register with the numer of nanoseconds + * per clock tick. + * + * Note: The value is calculated based on the current operating + * frequency 50MHz + */ + xemacps_write(lp->baseaddr, XEMACPS_1588INC_OFFSET, + (NS_PER_SEC/lp->ptpenetclk)); + + timecounter_init(&lp->clock, &lp->cycles, + ktime_to_ns(ktime_get_real())); + /* + * Synchronize our NIC clock against system wall clock. + */ + memset(&lp->compare, 0, sizeof(lp->compare)); + lp->compare.source = &lp->clock; + lp->compare.target = ktime_get_real; + lp->compare.num_samples = 10; + timecompare_update(&lp->compare, 0); + + /* Initialize hwstamp config */ + lp->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; + lp->hwtstamp_config.tx_type = HWTSTAMP_TX_OFF; + +} +#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */ + +/** + * xemacps_init_hw - Initialize hardware to known good state + * @lp: local device instance pointer + **/ +static void xemacps_init_hw(struct net_local *lp) +{ + u32 regval; + + xemacps_reset_hw(lp); + xemacps_set_hwaddr(lp); + + /* network configuration */ + regval = 0; + regval |= XEMACPS_NWCFG_FDEN_MASK; + regval |= XEMACPS_NWCFG_RXCHKSUMEN_MASK; + regval |= XEMACPS_NWCFG_PAUSECOPYDI_MASK; + regval |= XEMACPS_NWCFG_FCSREM_MASK; + regval |= XEMACPS_NWCFG_PAUSEEN_MASK; + regval |= XEMACPS_NWCFG_100_MASK; + regval |= XEMACPS_NWCFG_HDRXEN_MASK; + + regval |= (MDC_DIV_224 << XEMACPS_NWCFG_MDC_SHIFT_MASK); + if (lp->ndev->flags & IFF_PROMISC) /* copy all */ + regval |= XEMACPS_NWCFG_COPYALLEN_MASK; + if (!(lp->ndev->flags & IFF_BROADCAST)) /* No broadcast */ + regval |= XEMACPS_NWCFG_BCASTDI_MASK; + xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET, regval); + + /* Init TX and RX DMA Q address */ + xemacps_write(lp->baseaddr, XEMACPS_RXQBASE_OFFSET, lp->rx_bd_dma); + xemacps_write(lp->baseaddr, XEMACPS_TXQBASE_OFFSET, lp->tx_bd_dma); + + /* DMACR configurations */ + regval = (((XEMACPS_RX_BUF_SIZE / XEMACPS_RX_BUF_UNIT) + + ((XEMACPS_RX_BUF_SIZE % XEMACPS_RX_BUF_UNIT) ? 1 : 0)) << + XEMACPS_DMACR_RXBUF_SHIFT); + regval |= XEMACPS_DMACR_RXSIZE_MASK; + regval |= XEMACPS_DMACR_TXSIZE_MASK; + regval |= XEMACPS_DMACR_TCPCKSUM_MASK; +#ifdef __LITTLE_ENDIAN + regval &= ~XEMACPS_DMACR_ENDIAN_MASK; +#endif +#ifdef __BIG_ENDIAN + regval |= XEMACPS_DMACR_ENDIAN_MASK; +#endif + regval |= XEMACPS_DMACR_BLENGTH_INCR16; + xemacps_write(lp->baseaddr, XEMACPS_DMACR_OFFSET, regval); + + /* Enable TX, RX and MDIO port */ + regval = 0; + regval |= XEMACPS_NWCTRL_MDEN_MASK; + regval |= XEMACPS_NWCTRL_TXEN_MASK; + regval |= XEMACPS_NWCTRL_RXEN_MASK; + xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, regval); + +#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP + /* Initialize the Time Stamp Unit */ + xemacps_init_tsu(lp); +#endif + + /* Enable interrupts */ + regval = XEMACPS_IXR_ALL_MASK; + xemacps_write(lp->baseaddr, XEMACPS_IER_OFFSET, regval); +} + +/** + * xemacps_resetrx_for_no_rxdata - Resets the Rx if there is no data + * for a while (presently 100 msecs) + * @data: Used for net_local instance pointer + **/ +static void xemacps_resetrx_for_no_rxdata(unsigned long data) +{ + struct net_local *lp = (struct net_local *)data; + unsigned long regctrl; + unsigned long tempcntr; + unsigned long flags; + + tempcntr = xemacps_read(lp->baseaddr, XEMACPS_RXCNT_OFFSET); + if ((!tempcntr) && (!(lp->lastrxfrmscntr))) { + spin_lock_irqsave(&lp->nwctrlreg_lock, flags); + regctrl = xemacps_read(lp->baseaddr, + XEMACPS_NWCTRL_OFFSET); + regctrl &= (~XEMACPS_NWCTRL_RXEN_MASK); + xemacps_write(lp->baseaddr, + XEMACPS_NWCTRL_OFFSET, regctrl); + regctrl = xemacps_read(lp->baseaddr, XEMACPS_NWCTRL_OFFSET); + regctrl |= (XEMACPS_NWCTRL_RXEN_MASK); + xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, regctrl); + spin_unlock_irqrestore(&lp->nwctrlreg_lock, flags); + } + lp->lastrxfrmscntr = tempcntr; +} + +/** + * xemacps_update_stats - Update the statistic structure entries from + * the corresponding emacps hardware statistic registers + * @data: Used for net_local instance pointer + **/ +static void xemacps_update_stats(unsigned long data) +{ + struct net_local *lp = (struct net_local *)data; + struct net_device_stats *nstat = &lp->stats; + u32 cnt; + + cnt = xemacps_read(lp->baseaddr, XEMACPS_RXUNDRCNT_OFFSET); + nstat->rx_errors += cnt; + nstat->rx_length_errors += cnt; + + cnt = xemacps_read(lp->baseaddr, XEMACPS_RXOVRCNT_OFFSET); + nstat->rx_errors += cnt; + nstat->rx_length_errors += cnt; + + cnt = xemacps_read(lp->baseaddr, XEMACPS_RXJABCNT_OFFSET); + nstat->rx_errors += cnt; + nstat->rx_length_errors += cnt; + + cnt = xemacps_read(lp->baseaddr, XEMACPS_RXFCSCNT_OFFSET); + nstat->rx_errors += cnt; + nstat->rx_crc_errors += cnt; + + cnt = xemacps_read(lp->baseaddr, XEMACPS_RXLENGTHCNT_OFFSET); + nstat->rx_errors += cnt; + nstat->rx_length_errors += cnt; + + cnt = xemacps_read(lp->baseaddr, XEMACPS_RXALIGNCNT_OFFSET); + nstat->rx_errors += cnt; + nstat->rx_frame_errors += cnt; + + cnt = xemacps_read(lp->baseaddr, XEMACPS_RXRESERRCNT_OFFSET); + nstat->rx_errors += cnt; + nstat->rx_missed_errors += cnt; + + cnt = xemacps_read(lp->baseaddr, XEMACPS_RXORCNT_OFFSET); + nstat->rx_errors += cnt; + nstat->rx_fifo_errors += cnt; + + cnt = xemacps_read(lp->baseaddr, XEMACPS_TXURUNCNT_OFFSET); + nstat->tx_errors += cnt; + nstat->tx_fifo_errors += cnt; + + cnt = xemacps_read(lp->baseaddr, XEMACPS_SNGLCOLLCNT_OFFSET); + nstat->collisions += cnt; + + cnt = xemacps_read(lp->baseaddr, XEMACPS_MULTICOLLCNT_OFFSET); + nstat->collisions += cnt; + + cnt = xemacps_read(lp->baseaddr, XEMACPS_EXCESSCOLLCNT_OFFSET); + nstat->tx_errors += cnt; + nstat->tx_aborted_errors += cnt; + nstat->collisions += cnt; + + cnt = xemacps_read(lp->baseaddr, XEMACPS_LATECOLLCNT_OFFSET); + nstat->tx_errors += cnt; + nstat->collisions += cnt; + + cnt = xemacps_read(lp->baseaddr, XEMACPS_CSENSECNT_OFFSET); + nstat->tx_errors += cnt; + nstat->tx_carrier_errors += cnt; +} + +/** + * xemacps_gen_purpose_timerhandler - Timer handler that is called at regular + * intervals upon expiry of the gen_purpose_timer defined in net_local struct. + * @data: Used for net_local instance pointer + * + * This timer handler is used to update the statistics by calling the API + * xemacps_update_stats. The statistics register can typically overflow pretty + * quickly under heavy load conditions. This timer is used to periodically + * read the stats registers and update the corresponding stats structure + * entries. The stats registers when read reset to 0. + **/ +static void xemacps_gen_purpose_timerhandler(unsigned long data) +{ + struct net_local *lp = (struct net_local *)data; + + xemacps_update_stats(data); + xemacps_resetrx_for_no_rxdata(data); + mod_timer(&(lp->gen_purpose_timer), + jiffies + msecs_to_jiffies(XEAMCPS_GEN_PURPOSE_TIMER_LOAD)); +} + +/** + * xemacps_open - Called when a network device is made active + * @ndev: network interface device structure + * return 0 on success, negative value if error + * + * The open entry point is called when a network interface is made active + * by the system (IFF_UP). At this point all resources needed for transmit + * and receive operations are allocated, the interrupt handler is + * registered with OS, the watchdog timer is started, and the stack is + * notified that the interface is ready. + * + * note: if error(s), allocated resources before error require to be + * released or system issues (such as memory) leak might happen. + **/ +static int xemacps_open(struct net_device *ndev) +{ + struct net_local *lp = netdev_priv(ndev); + int rc; + + dev_dbg(&lp->pdev->dev, "open\n"); + if (!is_valid_ether_addr(ndev->dev_addr)) + return -EADDRNOTAVAIL; + + rc = xemacps_descriptor_init(lp); + if (rc) { + dev_err(&lp->pdev->dev, + "Unable to allocate DMA memory, rc %d\n", rc); + return rc; + } + + rc = pm_runtime_get_sync(&lp->pdev->dev); + if (rc < 0) { + dev_err(&lp->pdev->dev, + "pm_runtime_get_sync() failed, rc %d\n", rc); + goto err_free_rings; + } + + xemacps_init_hw(lp); + rc = xemacps_mii_probe(ndev); + if (rc != 0) { + dev_err(&lp->pdev->dev, + "%s mii_probe fail.\n", lp->mii_bus->name); + if (rc == (-2)) { + mdiobus_unregister(lp->mii_bus); + kfree(lp->mii_bus->irq); + mdiobus_free(lp->mii_bus); + } + rc = -ENXIO; + goto err_pm_put; + } + + setup_timer(&(lp->gen_purpose_timer), xemacps_gen_purpose_timerhandler, + (unsigned long)lp); + mod_timer(&(lp->gen_purpose_timer), + jiffies + msecs_to_jiffies(XEAMCPS_GEN_PURPOSE_TIMER_LOAD)); + + napi_enable(&lp->napi); + netif_carrier_on(ndev); + netif_start_queue(ndev); + tasklet_enable(&lp->tx_bdreclaim_tasklet); + + return 0; + +err_pm_put: + xemacps_reset_hw(lp); + pm_runtime_put(&lp->pdev->dev); +err_free_rings: + xemacps_descriptor_free(lp); + + return rc; +} + +/** + * xemacps_close - disable a network interface + * @ndev: network interface device structure + * return 0 + * + * The close entry point is called when a network interface is de-activated + * by OS. The hardware is still under the driver control, but needs to be + * disabled. A global MAC reset is issued to stop the hardware, and all + * transmit and receive resources are freed. + **/ +static int xemacps_close(struct net_device *ndev) +{ + struct net_local *lp = netdev_priv(ndev); + + del_timer_sync(&(lp->gen_purpose_timer)); + netif_stop_queue(ndev); + napi_disable(&lp->napi); + tasklet_disable(&lp->tx_bdreclaim_tasklet); + netif_carrier_off(ndev); + if (lp->phy_dev) + phy_disconnect(lp->phy_dev); + if (lp->gmii2rgmii_phy_node) + phy_disconnect(lp->gmii2rgmii_phy_dev); + xemacps_reset_hw(lp); + mdelay(500); + xemacps_descriptor_free(lp); + + pm_runtime_put(&lp->pdev->dev); + + return 0; +} + +/** + * xemacps_reinit_for_txtimeout - work queue scheduled for the tx timeout + * handling. + * @ndev: queue work structure + **/ +static void xemacps_reinit_for_txtimeout(struct work_struct *data) +{ + struct net_local *lp = container_of(data, struct net_local, + txtimeout_reinit); + int rc; + + netif_stop_queue(lp->ndev); + napi_disable(&lp->napi); + tasklet_disable(&lp->tx_bdreclaim_tasklet); + spin_lock_bh(&lp->tx_lock); + xemacps_reset_hw(lp); + spin_unlock_bh(&lp->tx_lock); + + if (lp->phy_dev) + phy_stop(lp->phy_dev); + + xemacps_descriptor_free(lp); + rc = xemacps_descriptor_init(lp); + if (rc) { + dev_err(&lp->pdev->dev, + "Unable to allocate DMA memory, rc %d\n", rc); + return; + } + + xemacps_init_hw(lp); + + lp->link = 0; + lp->speed = 0; + lp->duplex = -1; + + if (lp->phy_dev) + phy_start(lp->phy_dev); + + napi_enable(&lp->napi); + tasklet_enable(&lp->tx_bdreclaim_tasklet); + lp->ndev->trans_start = jiffies; + netif_wake_queue(lp->ndev); +} + +/** + * xemacps_tx_timeout - callback used when the transmitter has not made + * any progress for dev->watchdog ticks. + * @ndev: network interface device structure + **/ +static void xemacps_tx_timeout(struct net_device *ndev) +{ + struct net_local *lp = netdev_priv(ndev); + + dev_err(&lp->pdev->dev, "transmit timeout %lu ms, reseting...\n", + TX_TIMEOUT * 1000UL / HZ); + queue_work(lp->txtimeout_handler_wq, &lp->txtimeout_reinit); +} + +/** + * xemacps_set_mac_address - set network interface mac address + * @ndev: network interface device structure + * @addr: pointer to MAC address + * return 0 on success, negative value if error + **/ +static int xemacps_set_mac_address(struct net_device *ndev, void *addr) +{ + struct net_local *lp = netdev_priv(ndev); + struct sockaddr *hwaddr = (struct sockaddr *)addr; + + if (netif_running(ndev)) + return -EBUSY; + + if (!is_valid_ether_addr(hwaddr->sa_data)) + return -EADDRNOTAVAIL; + + dev_dbg(&lp->pdev->dev, "hwaddr 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + hwaddr->sa_data[0], hwaddr->sa_data[1], hwaddr->sa_data[2], + hwaddr->sa_data[3], hwaddr->sa_data[4], hwaddr->sa_data[5]); + + memcpy(ndev->dev_addr, hwaddr->sa_data, ndev->addr_len); + + xemacps_set_hwaddr(lp); + return 0; +} + +/** + * xemacps_clear_csum - Clear the csum field for transport protocols + * @skb: socket buffer + * @ndev: network interface device structure + * return 0 on success, other value if error + **/ +static int xemacps_clear_csum(struct sk_buff *skb, struct net_device *ndev) +{ + /* Only run for packets requiring a checksum. */ + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + if (unlikely(skb_cow_head(skb, 0))) + return -1; + + *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0; + + return 0; +} + +/** + * xemacps_start_xmit - transmit a packet (called by kernel) + * @skb: socket buffer + * @ndev: network interface device structure + * return 0 on success, other value if error + **/ +static int xemacps_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct net_local *lp = netdev_priv(ndev); + dma_addr_t mapping; + unsigned int nr_frags, len; + int i; + u32 regval; + void *virt_addr; + skb_frag_t *frag; + struct xemacps_bd *cur_p; + unsigned long flags; + u32 bd_tail; + + nr_frags = skb_shinfo(skb)->nr_frags + 1; + spin_lock_bh(&lp->tx_lock); + + if (nr_frags > lp->tx_bd_freecnt) { + netif_stop_queue(ndev); /* stop send queue */ + spin_unlock_bh(&lp->tx_lock); + return NETDEV_TX_BUSY; + } + + if(xemacps_clear_csum(skb,ndev)) { + spin_unlock_bh(&lp->tx_lock); + kfree(skb); + return NETDEV_TX_OK; + } + + bd_tail = lp->tx_bd_tail; + cur_p = &lp->tx_bd[bd_tail]; + lp->tx_bd_freecnt -= nr_frags; + frag = &skb_shinfo(skb)->frags[0]; + + for (i = 0; i < nr_frags; i++) { + if (i == 0) { + len = skb_headlen(skb); + mapping = dma_map_single(&lp->pdev->dev, skb->data, + len, DMA_TO_DEVICE); + } else { + len = skb_frag_size(frag); + virt_addr = skb_frag_address(frag); + mapping = dma_map_single(&lp->pdev->dev, virt_addr, + len, DMA_TO_DEVICE); + frag++; + skb_get(skb); + } + + lp->tx_skb[lp->tx_bd_tail].skb = skb; + lp->tx_skb[lp->tx_bd_tail].mapping = mapping; + lp->tx_skb[lp->tx_bd_tail].len = len; + cur_p->addr = mapping; + + /* preserve critical status bits */ + regval = cur_p->ctrl; + regval &= (XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK); + /* update length field */ + regval |= ((regval & ~XEMACPS_TXBUF_LEN_MASK) | len); + /* commit second to last buffer to hardware */ + if (i != 0) + regval &= ~XEMACPS_TXBUF_USED_MASK; + /* last fragment of this packet? */ + if (i == (nr_frags - 1)) + regval |= XEMACPS_TXBUF_LAST_MASK; + cur_p->ctrl = regval; + + lp->tx_bd_tail++; + lp->tx_bd_tail = lp->tx_bd_tail % XEMACPS_SEND_BD_CNT; + cur_p = &(lp->tx_bd[lp->tx_bd_tail]); + } + wmb(); + + /* commit first buffer to hardware -- do this after + * committing the other buffers to avoid an underrun */ + cur_p = &lp->tx_bd[bd_tail]; + regval = cur_p->ctrl; + regval &= ~XEMACPS_TXBUF_USED_MASK; + cur_p->ctrl = regval; + wmb(); + + spin_lock_irqsave(&lp->nwctrlreg_lock, flags); + regval = xemacps_read(lp->baseaddr, XEMACPS_NWCTRL_OFFSET); + xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, + (regval | XEMACPS_NWCTRL_STARTTX_MASK)); + spin_unlock_irqrestore(&lp->nwctrlreg_lock, flags); + + spin_unlock_bh(&lp->tx_lock); + ndev->trans_start = jiffies; + return 0; +} + +/* + * Get the MAC Address bit from the specified position + */ +static unsigned get_bit(u8 *mac, unsigned bit) +{ + unsigned byte; + + byte = mac[bit / 8]; + byte >>= (bit & 0x7); + byte &= 1; + + return byte; +} + +/* + * Calculate a GEM MAC Address hash index + */ +static unsigned calc_mac_hash(u8 *mac) +{ + int index_bit, mac_bit; + unsigned hash_index; + + hash_index = 0; + mac_bit = 5; + for (index_bit = 5; index_bit >= 0; index_bit--) { + hash_index |= (get_bit(mac, mac_bit) ^ + get_bit(mac, mac_bit + 6) ^ + get_bit(mac, mac_bit + 12) ^ + get_bit(mac, mac_bit + 18) ^ + get_bit(mac, mac_bit + 24) ^ + get_bit(mac, mac_bit + 30) ^ + get_bit(mac, mac_bit + 36) ^ + get_bit(mac, mac_bit + 42)) + << index_bit; + mac_bit--; + } + + return hash_index; +} + +/** + * xemacps_set_hashtable - Add multicast addresses to the internal + * multicast-hash table. Called from xemac_set_rx_mode(). + * @ndev: network interface device structure + * + * The hash address register is 64 bits long and takes up two + * locations in the memory map. The least significant bits are stored + * in EMAC_HSL and the most significant bits in EMAC_HSH. + * + * The unicast hash enable and the multicast hash enable bits in the + * network configuration register enable the reception of hash matched + * frames. The destination address is reduced to a 6 bit index into + * the 64 bit hash register using the following hash function. The + * hash function is an exclusive or of every sixth bit of the + * destination address. + * + * hi[5] = da[5] ^ da[11] ^ da[17] ^ da[23] ^ da[29] ^ da[35] ^ da[41] ^ da[47] + * hi[4] = da[4] ^ da[10] ^ da[16] ^ da[22] ^ da[28] ^ da[34] ^ da[40] ^ da[46] + * hi[3] = da[3] ^ da[09] ^ da[15] ^ da[21] ^ da[27] ^ da[33] ^ da[39] ^ da[45] + * hi[2] = da[2] ^ da[08] ^ da[14] ^ da[20] ^ da[26] ^ da[32] ^ da[38] ^ da[44] + * hi[1] = da[1] ^ da[07] ^ da[13] ^ da[19] ^ da[25] ^ da[31] ^ da[37] ^ da[43] + * hi[0] = da[0] ^ da[06] ^ da[12] ^ da[18] ^ da[24] ^ da[30] ^ da[36] ^ da[42] + * + * da[0] represents the least significant bit of the first byte + * received, that is, the multicast/unicast indicator, and da[47] + * represents the most significant bit of the last byte received. If + * the hash index, hi[n], points to a bit that is set in the hash + * register then the frame will be matched according to whether the + * frame is multicast or unicast. A multicast match will be signalled + * if the multicast hash enable bit is set, da[0] is 1 and the hash + * index points to a bit set in the hash register. A unicast match + * will be signalled if the unicast hash enable bit is set, da[0] is 0 + * and the hash index points to a bit set in the hash register. To + * receive all multicast frames, the hash register should be set with + * all ones and the multicast hash enable bit should be set in the + * network configuration register. + **/ +static void xemacps_set_hashtable(struct net_device *ndev) +{ + struct netdev_hw_addr *curr; + u32 regvalh, regvall, hash_index; + u8 *mc_addr; + struct net_local *lp; + + lp = netdev_priv(ndev); + + regvalh = regvall = 0; + + netdev_for_each_mc_addr(curr, ndev) { + if (!curr) /* end of list */ + break; + mc_addr = curr->addr; + hash_index = calc_mac_hash(mc_addr); + + if (hash_index >= XEMACPS_MAX_HASH_BITS) { + dev_err(&lp->pdev->dev, + "hash calculation out of range %d\n", + hash_index); + break; + } + if (hash_index < 32) + regvall |= (1 << hash_index); + else + regvalh |= (1 << (hash_index - 32)); + } + + xemacps_write(lp->baseaddr, XEMACPS_HASHL_OFFSET, regvall); + xemacps_write(lp->baseaddr, XEMACPS_HASHH_OFFSET, regvalh); +} + +/** + * xemacps_set_rx_mode - enable/disable promiscuous and multicast modes + * @ndev: network interface device structure + **/ +static void xemacps_set_rx_mode(struct net_device *ndev) +{ + struct net_local *lp = netdev_priv(ndev); + u32 regval; + + regval = xemacps_read(lp->baseaddr, XEMACPS_NWCFG_OFFSET); + + /* promisc mode */ + if (ndev->flags & IFF_PROMISC) + regval |= XEMACPS_NWCFG_COPYALLEN_MASK; + if (!(ndev->flags & IFF_PROMISC)) + regval &= ~XEMACPS_NWCFG_COPYALLEN_MASK; + + /* All multicast mode */ + if (ndev->flags & IFF_ALLMULTI) { + regval |= XEMACPS_NWCFG_MCASTHASHEN_MASK; + xemacps_write(lp->baseaddr, XEMACPS_HASHL_OFFSET, ~0UL); + xemacps_write(lp->baseaddr, XEMACPS_HASHH_OFFSET, ~0UL); + /* Specific multicast mode */ + } else if ((ndev->flags & IFF_MULTICAST) + && (netdev_mc_count(ndev) > 0)) { + regval |= XEMACPS_NWCFG_MCASTHASHEN_MASK; + xemacps_set_hashtable(ndev); + /* Disable multicast mode */ + } else { + xemacps_write(lp->baseaddr, XEMACPS_HASHL_OFFSET, 0x0); + xemacps_write(lp->baseaddr, XEMACPS_HASHH_OFFSET, 0x0); + regval &= ~XEMACPS_NWCFG_MCASTHASHEN_MASK; + } + + /* broadcast mode */ + if (ndev->flags & IFF_BROADCAST) + regval &= ~XEMACPS_NWCFG_BCASTDI_MASK; + /* No broadcast */ + if (!(ndev->flags & IFF_BROADCAST)) + regval |= XEMACPS_NWCFG_BCASTDI_MASK; + + xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET, regval); +} + +#define MIN_MTU 60 +#define MAX_MTU 1500 +/** + * xemacps_change_mtu - Change maximum transfer unit + * @ndev: network interface device structure + * @new_mtu: new vlaue for maximum frame size + * return: 0 on success, negative value if error. + **/ +static int xemacps_change_mtu(struct net_device *ndev, int new_mtu) +{ + if ((new_mtu < MIN_MTU) || + ((new_mtu + ndev->hard_header_len) > MAX_MTU)) + return -EINVAL; + + ndev->mtu = new_mtu; /* change mtu in net_device structure */ + return 0; +} + +/** + * xemacps_get_settings - get device specific settings. + * Usage: Issue "ethtool ethX" under linux prompt. + * @ndev: network device + * @ecmd: ethtool command structure + * return: 0 on success, negative value if error. + **/ +static int +xemacps_get_settings(struct net_device *ndev, struct ethtool_cmd *ecmd) +{ + struct net_local *lp = netdev_priv(ndev); + struct phy_device *phydev = lp->phy_dev; + + if (!phydev) + return -ENODEV; + + return phy_ethtool_gset(phydev, ecmd); +} + +/** + * xemacps_set_settings - set device specific settings. + * Usage: Issue "ethtool -s ethX speed 1000" under linux prompt + * to change speed + * @ndev: network device + * @ecmd: ethtool command structure + * return: 0 on success, negative value if error. + **/ +static int +xemacps_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd) +{ + struct net_local *lp = netdev_priv(ndev); + struct phy_device *phydev = lp->phy_dev; + + if (!phydev) + return -ENODEV; + + return phy_ethtool_sset(phydev, ecmd); +} + +/** + * xemacps_get_drvinfo - report driver information + * Usage: Issue "ethtool -i ethX" under linux prompt + * @ndev: network device + * @ed: device driver information structure + **/ +static void +xemacps_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *ed) +{ + struct net_local *lp = netdev_priv(ndev); + + memset(ed, 0, sizeof(struct ethtool_drvinfo)); + strcpy(ed->driver, lp->pdev->dev.driver->name); + strcpy(ed->version, DRIVER_VERSION); +} + +/** + * xemacps_get_ringparam - get device dma ring information. + * Usage: Issue "ethtool -g ethX" under linux prompt + * @ndev: network device + * @erp: ethtool ring parameter structure + **/ +static void +xemacps_get_ringparam(struct net_device *ndev, struct ethtool_ringparam *erp) +{ + memset(erp, 0, sizeof(struct ethtool_ringparam)); + + erp->rx_max_pending = XEMACPS_RECV_BD_CNT; + erp->tx_max_pending = XEMACPS_SEND_BD_CNT; + erp->rx_pending = 0; + erp->tx_pending = 0; +} + +/** + * xemacps_get_wol - get device wake on lan status + * Usage: Issue "ethtool ethX" under linux prompt + * @ndev: network device + * @ewol: wol status + **/ +static void +xemacps_get_wol(struct net_device *ndev, struct ethtool_wolinfo *ewol) +{ + struct net_local *lp = netdev_priv(ndev); + u32 regval; + + ewol->supported = WAKE_MAGIC | WAKE_ARP | WAKE_UCAST | WAKE_MCAST; + + regval = xemacps_read(lp->baseaddr, XEMACPS_WOL_OFFSET); + if (regval | XEMACPS_WOL_MCAST_MASK) + ewol->wolopts |= WAKE_MCAST; + if (regval | XEMACPS_WOL_ARP_MASK) + ewol->wolopts |= WAKE_ARP; + if (regval | XEMACPS_WOL_SPEREG1_MASK) + ewol->wolopts |= WAKE_UCAST; + if (regval | XEMACPS_WOL_MAGIC_MASK) + ewol->wolopts |= WAKE_MAGIC; + +} + +/** + * xemacps_set_wol - set device wake on lan configuration + * Usage: Issue "ethtool -s ethX wol u|m|b|g" under linux prompt to enable + * specified type of packet. + * Usage: Issue "ethtool -s ethX wol d" under linux prompt to disable + * this feature. + * @ndev: network device + * @ewol: wol status + * return 0 on success, negative value if not supported + **/ +static int +xemacps_set_wol(struct net_device *ndev, struct ethtool_wolinfo *ewol) +{ + struct net_local *lp = netdev_priv(ndev); + u32 regval; + + if (ewol->wolopts & ~(WAKE_MAGIC | WAKE_ARP | WAKE_UCAST | WAKE_MCAST)) + return -EOPNOTSUPP; + + regval = xemacps_read(lp->baseaddr, XEMACPS_WOL_OFFSET); + regval &= ~(XEMACPS_WOL_MCAST_MASK | XEMACPS_WOL_ARP_MASK | + XEMACPS_WOL_SPEREG1_MASK | XEMACPS_WOL_MAGIC_MASK); + + if (ewol->wolopts & WAKE_MAGIC) + regval |= XEMACPS_WOL_MAGIC_MASK; + if (ewol->wolopts & WAKE_ARP) + regval |= XEMACPS_WOL_ARP_MASK; + if (ewol->wolopts & WAKE_UCAST) + regval |= XEMACPS_WOL_SPEREG1_MASK; + if (ewol->wolopts & WAKE_MCAST) + regval |= XEMACPS_WOL_MCAST_MASK; + + xemacps_write(lp->baseaddr, XEMACPS_WOL_OFFSET, regval); + + return 0; +} + +/** + * xemacps_get_pauseparam - get device pause status + * Usage: Issue "ethtool -a ethX" under linux prompt + * @ndev: network device + * @epauseparam: pause parameter + * + * note: hardware supports only tx flow control + **/ +static void +xemacps_get_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *epauseparm) +{ + struct net_local *lp = netdev_priv(ndev); + u32 regval; + + epauseparm->autoneg = 0; + epauseparm->rx_pause = 0; + + regval = xemacps_read(lp->baseaddr, XEMACPS_NWCFG_OFFSET); + epauseparm->tx_pause = regval & XEMACPS_NWCFG_PAUSEEN_MASK; +} + +/** + * xemacps_set_pauseparam - set device pause parameter(flow control) + * Usage: Issue "ethtool -A ethX tx on|off" under linux prompt + * @ndev: network device + * @epauseparam: pause parameter + * return 0 on success, negative value if not supported + * + * note: hardware supports only tx flow control + **/ +static int +xemacps_set_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *epauseparm) +{ + struct net_local *lp = netdev_priv(ndev); + u32 regval; + + if (netif_running(ndev)) { + dev_err(&lp->pdev->dev, + "Please stop netif before apply configruation\n"); + return -EFAULT; + } + + regval = xemacps_read(lp->baseaddr, XEMACPS_NWCFG_OFFSET); + + if (epauseparm->tx_pause) + regval |= XEMACPS_NWCFG_PAUSEEN_MASK; + if (!(epauseparm->tx_pause)) + regval &= ~XEMACPS_NWCFG_PAUSEEN_MASK; + + xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET, regval); + return 0; +} + +/** + * xemacps_get_stats - get device statistic raw data in 64bit mode + * @ndev: network device + **/ +static struct net_device_stats +*xemacps_get_stats(struct net_device *ndev) +{ + struct net_local *lp = netdev_priv(ndev); + struct net_device_stats *nstat = &lp->stats; + + xemacps_update_stats((unsigned long)lp); + return nstat; +} + +static struct ethtool_ops xemacps_ethtool_ops = { + .get_settings = xemacps_get_settings, + .set_settings = xemacps_set_settings, + .get_drvinfo = xemacps_get_drvinfo, + .get_link = ethtool_op_get_link, /* ethtool default */ + .get_ringparam = xemacps_get_ringparam, + .get_wol = xemacps_get_wol, + .set_wol = xemacps_set_wol, + .get_pauseparam = xemacps_get_pauseparam, + .set_pauseparam = xemacps_set_pauseparam, +}; + +#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP +static int xemacps_hwtstamp_ioctl(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + struct hwtstamp_config config; + struct net_local *lp; + u32 regval; + + lp = netdev_priv(netdev); + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + /* reserved for future extensions */ + if (config.flags) + return -EINVAL; + + if ((config.tx_type != HWTSTAMP_TX_OFF) && + (config.tx_type != HWTSTAMP_TX_ON)) + return -ERANGE; + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + config.rx_filter = HWTSTAMP_FILTER_ALL; + regval = xemacps_read(lp->baseaddr, XEMACPS_NWCTRL_OFFSET); + xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, + (regval | XEMACPS_NWCTRL_RXTSTAMP_MASK)); + break; + default: + return -ERANGE; + } + + config.tx_type = HWTSTAMP_TX_ON; + lp->hwtstamp_config = config; + + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? + -EFAULT : 0; +} +#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */ + +/** + * xemacps_ioctl - ioctl entry point + * @ndev: network device + * @rq: interface request ioctl + * @cmd: command code + * + * Called when user issues an ioctl request to the network device. + **/ +static int xemacps_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) +{ + struct net_local *lp = netdev_priv(ndev); + struct phy_device *phydev = lp->phy_dev; + + if (!netif_running(ndev)) + return -EINVAL; + + if (!phydev) + return -ENODEV; + + switch (cmd) { + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + return phy_mii_ioctl(phydev, rq, cmd); +#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP + case SIOCSHWTSTAMP: + return xemacps_hwtstamp_ioctl(ndev, rq, cmd); +#endif + default: + dev_info(&lp->pdev->dev, "ioctl %d not implemented.\n", cmd); + return -EOPNOTSUPP; + } + +} + +/** + * xemacps_probe - Platform driver probe + * @pdev: Pointer to platform device structure + * + * Return 0 on success, negative value if error + */ +static int xemacps_probe(struct platform_device *pdev) +{ + struct resource *r_mem = NULL; + struct resource *r_irq = NULL; + struct net_device *ndev; + struct net_local *lp; + u32 regval = 0; + int rc = -ENXIO; + + r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!r_mem || !r_irq) { + dev_err(&pdev->dev, "no IO resource defined.\n"); + return -ENXIO; + } + + ndev = alloc_etherdev(sizeof(*lp)); + if (!ndev) { + dev_err(&pdev->dev, "etherdev allocation failed.\n"); + return -ENOMEM; + } + + SET_NETDEV_DEV(ndev, &pdev->dev); + + lp = netdev_priv(ndev); + lp->pdev = pdev; + lp->ndev = ndev; + + spin_lock_init(&lp->tx_lock); + spin_lock_init(&lp->rx_lock); + spin_lock_init(&lp->nwctrlreg_lock); + + lp->baseaddr = devm_ioremap_resource(&pdev->dev, r_mem); + if (IS_ERR(lp->baseaddr)) { + dev_err(&pdev->dev, "failed to map baseaddress.\n"); + rc = PTR_ERR(lp->baseaddr); + goto err_out_free_netdev; + } + + dev_dbg(&lp->pdev->dev, "BASEADDRESS hw: %p virt: %p\n", + (void *)r_mem->start, lp->baseaddr); + + ndev->irq = platform_get_irq(pdev, 0); + + ndev->netdev_ops = &netdev_ops; + ndev->watchdog_timeo = TX_TIMEOUT; + ndev->ethtool_ops = &xemacps_ethtool_ops; + ndev->base_addr = r_mem->start; + ndev->features = NETIF_F_IP_CSUM | NETIF_F_SG; + netif_napi_add(ndev, &lp->napi, xemacps_rx_poll, XEMACPS_NAPI_WEIGHT); + + lp->ip_summed = CHECKSUM_UNNECESSARY; + + rc = register_netdev(ndev); + if (rc) { + dev_err(&pdev->dev, "Cannot register net device, aborting.\n"); + goto err_out_free_netdev; + } + + if (ndev->irq == 54) + lp->enetnum = 0; + else + lp->enetnum = 1; + + lp->aperclk = devm_clk_get(&pdev->dev, "aper_clk"); + if (IS_ERR(lp->aperclk)) { + dev_err(&pdev->dev, "aper_clk clock not found.\n"); + rc = PTR_ERR(lp->aperclk); + goto err_out_unregister_netdev; + } + lp->devclk = devm_clk_get(&pdev->dev, "ref_clk"); + if (IS_ERR(lp->devclk)) { + dev_err(&pdev->dev, "ref_clk clock not found.\n"); + rc = PTR_ERR(lp->devclk); + goto err_out_unregister_netdev; + } + + rc = clk_prepare_enable(lp->aperclk); + if (rc) { + dev_err(&pdev->dev, "Unable to enable APER clock.\n"); + goto err_out_unregister_netdev; + } + rc = clk_prepare_enable(lp->devclk); + if (rc) { + dev_err(&pdev->dev, "Unable to enable device clock.\n"); + goto err_out_clk_dis_aper; + } + + lp->clk_rate_change_nb.notifier_call = xemacps_clk_notifier_cb; + lp->clk_rate_change_nb.next = NULL; + if (clk_notifier_register(lp->devclk, &lp->clk_rate_change_nb)) + dev_warn(&pdev->dev, + "Unable to register clock notifier.\n"); + +#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP + prop = of_get_property(lp->pdev->dev.of_node, + "xlnx,ptp-enet-clock", NULL); + if (prop) + lp->ptpenetclk = (u32)be32_to_cpup(prop); + else + lp->ptpenetclk = 133333328; +#endif + + lp->phy_node = of_parse_phandle(lp->pdev->dev.of_node, + "phy-handle", 0); + lp->gmii2rgmii_phy_node = of_parse_phandle(lp->pdev->dev.of_node, + "gmii2rgmii-phy-handle", 0); + rc = of_get_phy_mode(lp->pdev->dev.of_node); + if (rc < 0) { + dev_err(&lp->pdev->dev, "error in getting phy i/f\n"); + goto err_out_unregister_clk_notifier; + } + + lp->phy_interface = rc; + + /* Set MDIO clock divider */ + regval = (MDC_DIV_224 << XEMACPS_NWCFG_MDC_SHIFT_MASK); + xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET, regval); + + + regval = XEMACPS_NWCTRL_MDEN_MASK; + xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, regval); + + rc = xemacps_mii_init(lp); + if (rc) { + dev_err(&lp->pdev->dev, "error in xemacps_mii_init\n"); + goto err_out_unregister_clk_notifier; + } + + xemacps_update_hwaddr(lp); + tasklet_init(&lp->tx_bdreclaim_tasklet, xemacps_tx_poll, + (unsigned long) ndev); + tasklet_disable(&lp->tx_bdreclaim_tasklet); + + lp->txtimeout_handler_wq = create_singlethread_workqueue(DRIVER_NAME); + INIT_WORK(&lp->txtimeout_reinit, xemacps_reinit_for_txtimeout); + + platform_set_drvdata(pdev, ndev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + dev_info(&lp->pdev->dev, "pdev->id %d, baseaddr 0x%08lx, irq %d\n", + pdev->id, ndev->base_addr, ndev->irq); + + rc = devm_request_irq(&pdev->dev, ndev->irq, &xemacps_interrupt, 0, + ndev->name, ndev); + if (rc) { + dev_err(&lp->pdev->dev, "Unable to request IRQ %p, error %d\n", + r_irq, rc); + goto err_out_unregister_clk_notifier; + } + + return 0; + +err_out_unregister_clk_notifier: + clk_notifier_unregister(lp->devclk, &lp->clk_rate_change_nb); + clk_disable_unprepare(lp->devclk); +err_out_clk_dis_aper: + clk_disable_unprepare(lp->aperclk); +err_out_unregister_netdev: + unregister_netdev(ndev); +err_out_free_netdev: + free_netdev(ndev); + platform_set_drvdata(pdev, NULL); + return rc; +} + +/** + * xemacps_remove - called when platform driver is unregistered + * @pdev: Pointer to the platform device structure + * + * return: 0 on success + */ +static int xemacps_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct net_local *lp; + + if (ndev) { + lp = netdev_priv(ndev); + + mdiobus_unregister(lp->mii_bus); + kfree(lp->mii_bus->irq); + mdiobus_free(lp->mii_bus); + unregister_netdev(ndev); + + clk_notifier_unregister(lp->devclk, &lp->clk_rate_change_nb); + if (!pm_runtime_suspended(&pdev->dev)) { + clk_disable_unprepare(lp->devclk); + clk_disable_unprepare(lp->aperclk); + } else { + clk_unprepare(lp->devclk); + clk_unprepare(lp->aperclk); + } + + free_netdev(ndev); + } + + return 0; +} + +#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP +/** + * xemacps_suspend - Suspend event + * @device: Pointer to device structure + * + * Return 0 + */ +static int xemacps_suspend(struct device *device) +{ + struct platform_device *pdev = container_of(device, + struct platform_device, dev); + struct net_device *ndev = platform_get_drvdata(pdev); + struct net_local *lp = netdev_priv(ndev); + + netif_device_detach(ndev); + if (!pm_runtime_suspended(device)) { + clk_disable(lp->devclk); + clk_disable(lp->aperclk); + } + return 0; +} + +/** + * xemacps_resume - Resume after previous suspend + * @pdev: Pointer to platform device structure + * + * Returns 0 on success, errno otherwise. + */ +static int xemacps_resume(struct device *device) +{ + struct platform_device *pdev = container_of(device, + struct platform_device, dev); + struct net_device *ndev = platform_get_drvdata(pdev); + struct net_local *lp = netdev_priv(ndev); + + if (!pm_runtime_suspended(device)) { + int ret; + + ret = clk_enable(lp->aperclk); + if (ret) + return ret; + + ret = clk_enable(lp->devclk); + if (ret) { + clk_disable(lp->aperclk); + return ret; + } + } + netif_device_attach(ndev); + return 0; +} +#endif /* ! CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM_RUNTIME +static int xemacps_runtime_idle(struct device *dev) +{ + return pm_schedule_suspend(dev, 1); +} + +static int xemacps_runtime_resume(struct device *device) +{ + int ret; + struct platform_device *pdev = container_of(device, + struct platform_device, dev); + struct net_device *ndev = platform_get_drvdata(pdev); + struct net_local *lp = netdev_priv(ndev); + + ret = clk_enable(lp->aperclk); + if (ret) + return ret; + + ret = clk_enable(lp->devclk); + if (ret) { + clk_disable(lp->aperclk); + return ret; + } + + return 0; +} + +static int xemacps_runtime_suspend(struct device *device) +{ + struct platform_device *pdev = container_of(device, + struct platform_device, dev); + struct net_device *ndev = platform_get_drvdata(pdev); + struct net_local *lp = netdev_priv(ndev); + + clk_disable(lp->devclk); + clk_disable(lp->aperclk); + return 0; +} +#endif /* CONFIG_PM_RUNTIME */ + +static const struct dev_pm_ops xemacps_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(xemacps_suspend, xemacps_resume) + SET_RUNTIME_PM_OPS(xemacps_runtime_suspend, xemacps_runtime_resume, + xemacps_runtime_idle) +}; +#define XEMACPS_PM (&xemacps_dev_pm_ops) +#else /* ! CONFIG_PM */ +#define XEMACPS_PM NULL +#endif /* ! CONFIG_PM */ + +static struct net_device_ops netdev_ops = { + .ndo_open = xemacps_open, + .ndo_stop = xemacps_close, + .ndo_start_xmit = xemacps_start_xmit, + .ndo_set_rx_mode = xemacps_set_rx_mode, + .ndo_set_mac_address = xemacps_set_mac_address, + .ndo_do_ioctl = xemacps_ioctl, + .ndo_change_mtu = xemacps_change_mtu, + .ndo_tx_timeout = xemacps_tx_timeout, + .ndo_get_stats = xemacps_get_stats, +}; + +static struct of_device_id xemacps_of_match[] = { + { .compatible = "xlnx,ps7-ethernet-1.00.a", }, + { /* end of table */} +}; +MODULE_DEVICE_TABLE(of, xemacps_of_match); + +static struct platform_driver xemacps_driver = { + .probe = xemacps_probe, + .remove = xemacps_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = xemacps_of_match, + .pm = XEMACPS_PM, + }, +}; + +module_platform_driver(xemacps_driver); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Xilinx Ethernet driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/ethernet/xilinx/xilinx_lltemac/Makefile b/drivers/net/ethernet/xilinx/xilinx_lltemac/Makefile new file mode 100644 index 0000000..cb2c736 --- /dev/null +++ b/drivers/net/ethernet/xilinx/xilinx_lltemac/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the Xilinx Tri-mode ethernet driver +# + +EXTRA_CFLAGS += -Idrivers/xilinx_common + +# The Linux adapter for the Xilinx driver code. +xilinx_temac-objs := xlltemac_main.o xlltemac.o xlltemac_control.o + +obj-$(CONFIG_XILINX_LLTEMAC) := xilinx_temac.o diff --git a/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac.c b/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac.c new file mode 100644 index 0000000..5bc92b5 --- /dev/null +++ b/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac.c @@ -0,0 +1,1387 @@ +/* $Id: */ +/****************************************************************************** +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" +* AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND +* SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, +* OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, +* APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION +* THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, +* AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE +* FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY +* WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE +* IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR +* REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF +* INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE. +* +* (c) Copyright 2005-2008 Xilinx Inc. +* All rights reserved. +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +* +******************************************************************************/ +/*****************************************************************************/ +/** + * + * @file xlltemac.c + * + * The XLlTemac driver. Functions in this file are the minimum required functions + * for this driver. See xlltemac.h for a detailed description of the driver. + * + *
+ * MODIFICATION HISTORY:
+ *
+ * Ver   Who  Date     Changes
+ * ----- ---- -------- -------------------------------------------------------
+ * 1.00a jvb  11/10/06 First release
+ * 
+ ******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include +#include +#include + +#include "xlltemac.h" + +/************************** Constant Definitions *****************************/ + + +/**************************** Type Definitions *******************************/ + + +/***************** Macros (Inline Functions) Definitions *********************/ + + +/************************** Function Prototypes ******************************/ + +static void InitHw(XLlTemac *InstancePtr); /* HW reset */ + +/************************** Variable Definitions *****************************/ + +xdbg_stmnt(int indent_on = 0; + + ) + xdbg_stmnt(u32 _xlltemac_rir_value; + + ) + +/*****************************************************************************/ +/** + * + * XLlTemac_CfgInitialize initializes a TEMAC channel along with the + * InstancePtr that references it. Each TEMAC channel is treated as a + * separate device from the point of view of this driver. + * + * The PHY is setup independently from the TEMAC. Use the MII or whatever other + * interface may be present for setup. + * + * @param InstancePtr references the memory instance to be associated with + * the TEMAC channel upon initialization. + * @param CfgPtr references the structure holding the hardware configuration + * for the TEMAC channel to initialize. + * @param EffectiveAddress is the processor address used to access the + * base address of the TEMAC channel. In systems with an MMU and virtual + * memory, EffectiveAddress is the virtual address mapped to the + * physical in ConfigPtr->Config.BaseAddress. In systems + * without an active MMU, EffectiveAddress should be set to the + * same value as ConfigPtr->Config.BaseAddress. + * + * @return XLlTemac_CfgInitialize returns XST_SUCCESS. + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + * + ******************************************************************************/ + int XLlTemac_CfgInitialize(XLlTemac *InstancePtr, + XLlTemac_Config *CfgPtr, u32 EffectiveAddress) +{ + /* Verify arguments */ + XASSERT_NONVOID(InstancePtr != NULL); + + /* Clear instance memory and make copy of configuration */ + memset(InstancePtr, 0, sizeof(XLlTemac)); + memcpy(&InstancePtr->Config, CfgPtr, sizeof(XLlTemac_Config)); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_CfgInitialize\n"); + /* Set device base address */ + InstancePtr->Config.BaseAddress = EffectiveAddress; + + /* Reset the hardware and set default options */ + InstancePtr->IsReady = XCOMPONENT_IS_READY; + + XLlTemac_Reset(InstancePtr, XTE_NORESET_HARD); + + xdbg_printf(XDBG_DEBUG_GENERAL, + "Temac_CfgInitialize: returning SUCCESS\n"); + return XST_SUCCESS; +} + + +/*****************************************************************************/ +/** + * XLlTemac_Start starts the TEMAC channel as follows: + * - Enable transmitter if XTE_TRANSMIT_ENABLE_OPTION is set + * - Enable receiver if XTE_RECEIVER_ENABLE_OPTION is set + * + * @param InstancePtr references the TEMAC channel on which to operate. + * + * @return N/A + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +void XLlTemac_Start(XLlTemac *InstancePtr) +{ + u32 Reg; + + /* Assert bad arguments and conditions */ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_VOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & + XTE_RDY_HARD_ACS_RDY_MASK); + + /* If already started, then there is nothing to do */ + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + return; + } + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_Start\n"); + /* Enable transmitter if not already enabled */ + if (InstancePtr->Options & XTE_TRANSMITTER_ENABLE_OPTION) { + xdbg_printf(XDBG_DEBUG_GENERAL, "enabling transmitter\n"); + Reg = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_TC_OFFSET); + if (!(Reg & XTE_TC_TX_MASK)) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "transmitter not enabled, enabling now\n"); + XLlTemac_WriteIndirectReg(InstancePtr->Config. + BaseAddress, XTE_TC_OFFSET, + Reg | XTE_TC_TX_MASK); + } + xdbg_printf(XDBG_DEBUG_GENERAL, "transmitter enabled\n"); + } + + /* Enable receiver */ + if (InstancePtr->Options & XTE_RECEIVER_ENABLE_OPTION) { + xdbg_printf(XDBG_DEBUG_GENERAL, "enabling receiver\n"); + Reg = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET); + if (!(Reg & XTE_RCW1_RX_MASK)) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "receiver not enabled, enabling now\n"); + + XLlTemac_WriteIndirectReg(InstancePtr->Config. + BaseAddress, XTE_RCW1_OFFSET, + Reg | XTE_RCW1_RX_MASK); + } + xdbg_printf(XDBG_DEBUG_GENERAL, "receiver enabled\n"); + } + + /* Mark as started */ + InstancePtr->IsStarted = XCOMPONENT_IS_STARTED; + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_Start: done\n"); +} + +/*****************************************************************************/ +/** + * XLlTemac_Stop gracefully stops the TEMAC channel as follows: + * - Disable all interrupts from this device + * - Disable the receiver + * + * XLlTemac_Stop does not modify any of the current device options. + * + * Since the transmitter is not disabled, frames currently in internal buffers + * or in process by a DMA engine are allowed to be transmitted. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * + * @return N/A + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +void XLlTemac_Stop(XLlTemac *InstancePtr) +{ + u32 Reg; + + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_VOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & + XTE_RDY_HARD_ACS_RDY_MASK); + + /* If already stopped, then there is nothing to do */ + if (InstancePtr->IsStarted == 0) { + return; + } + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_Stop\n"); + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_Stop: disabling interrupts\n"); + /* Disable interrupts */ + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, XTE_IE_OFFSET, 0); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_Stop: disabling receiver\n"); + /* Disable the receiver */ + Reg = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET); + Reg &= ~XTE_RCW1_RX_MASK; + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET, Reg); + + /* Stopping the receiver in mid-packet causes a dropped packet indication + * from HW. Clear it. + */ + /* get the interrupt pending register */ + Reg = XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, XTE_IP_OFFSET); + if (Reg & XTE_INT_RXRJECT_MASK) { + /* set the interrupt status register to clear the interrupt */ + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, + XTE_IS_OFFSET, XTE_INT_RXRJECT_MASK); + } + + /* Mark as stopped */ + InstancePtr->IsStarted = 0; + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_Stop: done\n"); +} + + +/*****************************************************************************/ +/** + * XLlTemac_Reset performs a reset of the TEMAC channel, specified by + * InstancePtr, or both channels if HardCoreAction is set to + * XTE_RESET_HARD. + * + * XLlTemac_Reset also resets the TEMAC channel's options to their default values. + * + * The calling software is responsible for re-configuring the TEMAC channel + * (if necessary) and restarting the MAC after the reset. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param HardCoreAction describes how XLlTemac_Reset should treat the hard core + * block of the TEMAC.

+ * + * If XTE_RESET_HARD is set to XTE_RESET_HARD, then XLlTemac_Reset asserts + * the reset signal to the hard core block which will reset both channels + * of the TEMAC. This, of course, will bork any activity that may be + * occuring on the other channel. So, be careful here.

+ * + * Otherwise, XLlTemac_Reset resets just the transmitter and receiver of + * this TEMAC channel. + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +void XLlTemac_Reset(XLlTemac *InstancePtr, int HardCoreAction) +{ + u32 Reg; + u32 TimeoutCount = 2; + + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_VOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & + XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_Reset\n"); + /* Stop the device and reset HW */ + XLlTemac_Stop(InstancePtr); + InstancePtr->Options = XTE_DEFAULT_OPTIONS; + + /* Reset the receiver */ + xdbg_printf(XDBG_DEBUG_GENERAL, "resetting the receiver\n"); + Reg = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET); + Reg |= XTE_RCW1_RST_MASK; + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET, Reg); + + /* Reset the transmitter */ + xdbg_printf(XDBG_DEBUG_GENERAL, "resetting the transmitter\n"); + Reg = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_TC_OFFSET); + Reg |= XTE_TC_RST_MASK; + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_TC_OFFSET, Reg); + + xdbg_printf(XDBG_DEBUG_GENERAL, "waiting until reset is done\n"); + /* Poll until the reset is done */ + while (Reg & (XTE_RCW1_RST_MASK | XTE_TC_RST_MASK)) { + Reg = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET); + Reg |= XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_TC_OFFSET); + } + + /* Reset hard core if required */ + /* Resetting hard core will cause both channels to reset :-( */ + if (HardCoreAction == XTE_RESET_HARD) { + xdbg_printf(XDBG_DEBUG_GENERAL, "hard reset\n"); + Reg = XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RAF_OFFSET); + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, + XTE_RAF_OFFSET, Reg | XTE_RAF_HTRST_MASK); + while (TimeoutCount && + (!(XLlTemac_ReadReg + (InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & XTE_RDY_HARD_ACS_RDY_MASK))) { + udelay(XTE_RESET_HARD_DELAY_US); + TimeoutCount--; + } + } + + /* Setup HW */ + InitHw(InstancePtr); +} + + +/****************************************************************************** + * InitHw (internal use only) performs a one-time setup of a TEMAC channel. The + * setup performed here only need to occur once after any reset. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +static void InitHw(XLlTemac *InstancePtr) +{ + u32 Reg; + + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_VOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & + XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac InitHw\n"); + /* Disable the receiver */ + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac InitHw\n"); + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac InitHw: disabling receiver\n"); + Reg = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET); + Reg &= ~XTE_RCW1_RX_MASK; + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET, Reg); + + /* + * Stopping the receiver in mid-packet causes a dropped packet + * indication from HW. Clear it. + */ + /* get the interrupt pending register */ + Reg = XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, XTE_IP_OFFSET); + if (Reg & XTE_INT_RXRJECT_MASK) { + /* + * set the interrupt status register to clear the pending + * interrupt + */ + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, + XTE_IS_OFFSET, XTE_INT_RXRJECT_MASK); + } + + /* Sync default options with HW but leave receiver and transmitter + * disabled. They get enabled with XLlTemac_Start() if + * XTE_TRANSMITTER_ENABLE_OPTION and XTE_RECEIVER_ENABLE_OPTION are set + */ + XLlTemac_SetOptions(InstancePtr, InstancePtr->Options & + ~(XTE_TRANSMITTER_ENABLE_OPTION | + XTE_RECEIVER_ENABLE_OPTION)); + + XLlTemac_ClearOptions(InstancePtr, ~InstancePtr->Options); + + /* Set default MDIO divisor */ + XLlTemac_PhySetMdioDivisor(InstancePtr, XTE_MDIO_DIV_DFT); + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac InitHw: done\n"); +} + +/*****************************************************************************/ +/** + * XLlTemac_SetMacAddress sets the MAC address for the TEMAC channel, specified + * by InstancePtr to the MAC address specified by AddressPtr. + * The TEMAC channel must be stopped before calling this function. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param AddressPtr is a reference to the 6-byte MAC address to set. + * + * @return On successful completion, XLlTemac_SetMacAddress returns XST_SUCCESS. + * Otherwise, if the TEMAC channel has not stopped, + * XLlTemac_SetMacAddress returns XST_DEVICE_IS_STARTED. + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +int XLlTemac_SetMacAddress(XLlTemac *InstancePtr, void *AddressPtr) +{ + u32 MacAddr; + u8 *Aptr = (u8 *) AddressPtr; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + XASSERT_NONVOID(AddressPtr != NULL); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_NONVOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & + XTE_RDY_HARD_ACS_RDY_MASK); + + /* Be sure device has been stopped */ + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + return (XST_DEVICE_IS_STARTED); + } + + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_SetMacAddress: setting mac address to: 0x%08x%8x%8x%8x%8x%8x\n", + Aptr[0], Aptr[1], Aptr[2], Aptr[3], Aptr[4], Aptr[5]); + /* + * Set the MAC bits [31:0] in UAW0 + * Having Aptr be unsigned type prevents the following operations from sign extending + */ + MacAddr = Aptr[0]; + MacAddr |= Aptr[1] << 8; + MacAddr |= Aptr[2] << 16; + MacAddr |= Aptr[3] << 24; + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_UAW0_OFFSET, MacAddr); + + /* There are reserved bits in UAW1 so don't affect them */ + MacAddr = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_UAW1_OFFSET); + MacAddr &= ~XTE_UAW1_UNICASTADDR_MASK; + + /* Set MAC bits [47:32] in UAW1 */ + MacAddr |= Aptr[4]; + MacAddr |= Aptr[5] << 8; + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_UAW1_OFFSET, MacAddr); + + return (XST_SUCCESS); +} + + +/*****************************************************************************/ +/** + * XLlTemac_GetMacAddress gets the MAC address for the TEMAC channel, specified + * by InstancePtr into the memory buffer specified by AddressPtr. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param AddressPtr references the memory buffer to store the retrieved MAC + * address. This memory buffer must be at least 6 bytes in length. + * + * @return N/A + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +void XLlTemac_GetMacAddress(XLlTemac *InstancePtr, void *AddressPtr) +{ + u32 MacAddr; + u8 *Aptr = (u8 *) AddressPtr; + + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_VOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & + XTE_RDY_HARD_ACS_RDY_MASK); + + /* Read MAC bits [31:0] in UAW0 */ + MacAddr = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_UAW0_OFFSET); + Aptr[0] = (u8) MacAddr; + Aptr[1] = (u8) (MacAddr >> 8); + Aptr[2] = (u8) (MacAddr >> 16); + Aptr[3] = (u8) (MacAddr >> 24); + + /* Read MAC bits [47:32] in UAW1 */ + MacAddr = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_UAW1_OFFSET); + Aptr[4] = (u8) MacAddr; + Aptr[5] = (u8) (MacAddr >> 8); +} + +/*****************************************************************************/ +/** + * XLlTemac_SetOptions enables the options, Options for the TEMAC channel, + * specified by InstancePtr. The TEMAC channel should be stopped with + * XLlTemac_Stop() before changing options. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param Options is a bitmask of OR'd XTE_*_OPTION values for options to + * set. Options not specified are not affected. + * + * @return On successful completion, XLlTemac_SetOptions returns XST_SUCCESS. + * Otherwise, if the device has not been stopped, XLlTemac_SetOptions + * returns XST_DEVICE_IS_STARTED. + * + * @note + * See xlltemac.h for a description of the available options. + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +int XLlTemac_SetOptions(XLlTemac *InstancePtr, u32 Options) +{ + u32 Reg; /* Generic register contents */ + u32 RegRcw1; /* Reflects original contents of RCW1 */ + u32 RegTc; /* Reflects original contents of TC */ + u32 RegNewRcw1; /* Reflects new contents of RCW1 */ + u32 RegNewTc; /* Reflects new contents of TC */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_NONVOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & + XTE_RDY_HARD_ACS_RDY_MASK); + + /* Be sure device has been stopped */ + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + return (XST_DEVICE_IS_STARTED); + } + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_SetOptions\n"); + /* Many of these options will change the RCW1 or TC registers. + * To reduce the amount of IO to the device, group these options here + * and change them all at once. + */ + + /* Grab current register contents */ + RegRcw1 = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET); + RegTc = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_TC_OFFSET); + RegNewRcw1 = RegRcw1; + RegNewTc = RegTc; + + xdbg_printf(XDBG_DEBUG_GENERAL, + "current control regs: RCW1: 0x%0x; TC: 0x%0x\n", RegRcw1, + RegTc); + xdbg_printf(XDBG_DEBUG_GENERAL, + "Options: 0x%0x; default options: 0x%0x\n", Options, + XTE_DEFAULT_OPTIONS); + + /* Turn on jumbo packet support for both Rx and Tx */ + if (Options & XTE_JUMBO_OPTION) { + RegNewTc |= XTE_TC_JUM_MASK; + RegNewRcw1 |= XTE_RCW1_JUM_MASK; + } + + /* Turn on VLAN packet support for both Rx and Tx */ + if (Options & XTE_VLAN_OPTION) { + RegNewTc |= XTE_TC_VLAN_MASK; + RegNewRcw1 |= XTE_RCW1_VLAN_MASK; + } + + /* Turn on FCS stripping on receive packets */ + if (Options & XTE_FCS_STRIP_OPTION) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "setOptions: enabling fcs stripping\n"); + RegNewRcw1 &= ~XTE_RCW1_FCS_MASK; + } + + /* Turn on FCS insertion on transmit packets */ + if (Options & XTE_FCS_INSERT_OPTION) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "setOptions: enabling fcs insertion\n"); + RegNewTc &= ~XTE_TC_FCS_MASK; + } + + /* Turn on length/type field checking on receive packets */ + if (Options & XTE_LENTYPE_ERR_OPTION) { + RegNewRcw1 &= ~XTE_RCW1_LT_DIS_MASK; + } + + /* Enable transmitter */ + if (Options & XTE_TRANSMITTER_ENABLE_OPTION) { + RegNewTc |= XTE_TC_TX_MASK; + } + + /* Enable receiver */ + if (Options & XTE_RECEIVER_ENABLE_OPTION) { + RegNewRcw1 |= XTE_RCW1_RX_MASK; + } + + /* Change the TC or RCW1 registers if they need to be modified */ + if (RegTc != RegNewTc) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "setOptions: writting tc: 0x%0x\n", RegNewTc); + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_TC_OFFSET, RegNewTc); + } + + if (RegRcw1 != RegNewRcw1) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "setOptions: writting rcw1: 0x%0x\n", RegNewRcw1); + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET, RegNewRcw1); + } + + /* Rest of options twiddle bits of other registers. Handle them one at + * a time + */ + + /* Turn on flow control */ + if (Options & XTE_FLOW_CONTROL_OPTION) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "setOptions: endabling flow control\n"); + Reg = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_FCC_OFFSET); + Reg |= XTE_FCC_FCRX_MASK; + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_FCC_OFFSET, Reg); + } + xdbg_printf(XDBG_DEBUG_GENERAL, + "setOptions: rcw1 is now (fcc): 0x%0x\n", + XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET)); + + /* Turn on promiscuous frame filtering (all frames are received ) */ + if (Options & XTE_PROMISC_OPTION) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "setOptions: endabling promiscuous mode\n"); + Reg = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_AFM_OFFSET); + Reg |= XTE_AFM_PM_MASK; + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_AFM_OFFSET, Reg); + } + xdbg_printf(XDBG_DEBUG_GENERAL, + "setOptions: rcw1 is now (afm): 0x%0x\n", + XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET)); + + /* Allow broadcast address filtering */ + if (Options & XTE_BROADCAST_OPTION) { + Reg = XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RAF_OFFSET); + Reg &= ~XTE_RAF_BCSTREJ_MASK; + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, + XTE_RAF_OFFSET, Reg); + } + xdbg_printf(XDBG_DEBUG_GENERAL, + "setOptions: rcw1 is now (raf): 0x%0x\n", + XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET)); + + /* Allow multicast address filtering */ + if (Options & XTE_MULTICAST_OPTION) { + Reg = XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RAF_OFFSET); + Reg &= ~XTE_RAF_MCSTREJ_MASK; + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, + XTE_RAF_OFFSET, Reg); + } + xdbg_printf(XDBG_DEBUG_GENERAL, + "setOptions: rcw1 is now (raf2): 0x%0x\n", + XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET)); + + /* The remaining options not handled here are managed elsewhere in the + * driver. No register modifications are needed at this time. Reflecting the + * option in InstancePtr->Options is good enough for now. + */ + + /* Set options word to its new value */ + InstancePtr->Options |= Options; + + xdbg_printf(XDBG_DEBUG_GENERAL, + "setOptions: rcw1 is now (end): 0x%0x\n", + XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET)); + xdbg_printf(XDBG_DEBUG_GENERAL, "setOptions: returning SUCCESS\n"); + return (XST_SUCCESS); +} + +/*****************************************************************************/ +/** + * XLlTemac_ClearOptions clears the options, Options for the TEMAC channel, + * specified by InstancePtr. The TEMAC channel should be stopped with + * XLlTemac_Stop() before changing options. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param Options is a bitmask of OR'd XTE_*_OPTION values for options to + * clear. Options not specified are not affected. + * + * @return On successful completion, XLlTemac_ClearOptions returns XST_SUCCESS. + * Otherwise, if the device has not been stopped, XLlTemac_ClearOptions + * returns XST_DEVICE_IS_STARTED. + * + * @note + * See xlltemac.h for a description of the available options. + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +int XLlTemac_ClearOptions(XLlTemac *InstancePtr, u32 Options) +{ + u32 Reg; /* Generic */ + u32 RegRcw1; /* Reflects original contents of RCW1 */ + u32 RegTc; /* Reflects original contents of TC */ + u32 RegNewRcw1; /* Reflects new contents of RCW1 */ + u32 RegNewTc; /* Reflects new contents of TC */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_NONVOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & + XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "Xtemac_ClearOptions: 0x%08x\n", + Options); + /* Be sure device has been stopped */ + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + return (XST_DEVICE_IS_STARTED); + } + + /* Many of these options will change the RCW1 or TC registers. + * Group these options here and change them all at once. What we are + * trying to accomplish is to reduce the amount of IO to the device + */ + + /* Grab current register contents */ + RegRcw1 = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET); + RegTc = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_TC_OFFSET); + RegNewRcw1 = RegRcw1; + RegNewTc = RegTc; + + /* Turn off jumbo packet support for both Rx and Tx */ + if (Options & XTE_JUMBO_OPTION) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "Xtemac_ClearOptions: disabling jumbo\n"); + RegNewTc &= ~XTE_TC_JUM_MASK; + RegNewRcw1 &= ~XTE_RCW1_JUM_MASK; + } + + /* Turn off VLAN packet support for both Rx and Tx */ + if (Options & XTE_VLAN_OPTION) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "Xtemac_ClearOptions: disabling vlan\n"); + RegNewTc &= ~XTE_TC_VLAN_MASK; + RegNewRcw1 &= ~XTE_RCW1_VLAN_MASK; + } + + /* Turn off FCS stripping on receive packets */ + if (Options & XTE_FCS_STRIP_OPTION) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "Xtemac_ClearOptions: disabling fcs strip\n"); + RegNewRcw1 |= XTE_RCW1_FCS_MASK; + } + + /* Turn off FCS insertion on transmit packets */ + if (Options & XTE_FCS_INSERT_OPTION) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "Xtemac_ClearOptions: disabling fcs insert\n"); + RegNewTc |= XTE_TC_FCS_MASK; + } + + /* Turn off length/type field checking on receive packets */ + if (Options & XTE_LENTYPE_ERR_OPTION) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "Xtemac_ClearOptions: disabling lentype err\n"); + RegNewRcw1 |= XTE_RCW1_LT_DIS_MASK; + } + + /* Disable transmitter */ + if (Options & XTE_TRANSMITTER_ENABLE_OPTION) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "Xtemac_ClearOptions: disabling transmitter\n"); + RegNewTc &= ~XTE_TC_TX_MASK; + } + + /* Disable receiver */ + if (Options & XTE_RECEIVER_ENABLE_OPTION) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "Xtemac_ClearOptions: disabling receiver\n"); + RegNewRcw1 &= ~XTE_RCW1_RX_MASK; + } + + /* Change the TC and RCW1 registers if they need to be + * modified + */ + if (RegTc != RegNewTc) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "Xtemac_ClearOptions: setting TC: 0x%0x\n", + RegNewTc); + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_TC_OFFSET, RegNewTc); + } + + if (RegRcw1 != RegNewRcw1) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "Xtemac_ClearOptions: setting RCW1: 0x%0x\n", + RegNewRcw1); + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET, RegNewRcw1); + } + + /* Rest of options twiddle bits of other registers. Handle them one at + * a time + */ + + /* Turn off flow control */ + if (Options & XTE_FLOW_CONTROL_OPTION) { + Reg = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_FCC_OFFSET); + Reg &= ~XTE_FCC_FCRX_MASK; + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_FCC_OFFSET, Reg); + } + + /* Turn off promiscuous frame filtering */ + if (Options & XTE_PROMISC_OPTION) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "Xtemac_ClearOptions: disabling promiscuous mode\n"); + Reg = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_AFM_OFFSET); + Reg &= ~XTE_AFM_PM_MASK; + xdbg_printf(XDBG_DEBUG_GENERAL, + "Xtemac_ClearOptions: setting AFM: 0x%0x\n", Reg); + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_AFM_OFFSET, Reg); + } + + /* Disable broadcast address filtering */ + if (Options & XTE_BROADCAST_OPTION) { + Reg = XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RAF_OFFSET); + Reg |= XTE_RAF_BCSTREJ_MASK; + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, + XTE_RAF_OFFSET, Reg); + } + + /* Disable multicast address filtering */ + if (Options & XTE_MULTICAST_OPTION) { + Reg = XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RAF_OFFSET); + Reg |= XTE_RAF_MCSTREJ_MASK; + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, + XTE_RAF_OFFSET, Reg); + } + + /* The remaining options not handled here are managed elsewhere in the + * driver. No register modifications are needed at this time. Reflecting the + * option in InstancePtr->Options is good enough for now. + */ + + /* Set options word to its new value */ + InstancePtr->Options &= ~Options; + + return (XST_SUCCESS); +} + +/*****************************************************************************/ +/** + * XLlTemac_GetOptions returns the current option settings. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * + * @return XLlTemac_GetOptions returns a bitmask of XTE_*_OPTION constants, + * each bit specifying an option that is currently active. + * + * @note + * See xlltemac.h for a description of the available options. + * + ******************************************************************************/ +u32 XLlTemac_GetOptions(XLlTemac *InstancePtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + return (InstancePtr->Options); +} + +/*****************************************************************************/ +/** + * XLlTemac_GetOperatingSpeed gets the current operating link speed. This may be + * the value set by XLlTemac_SetOperatingSpeed() or a hardware default. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * + * @return XLlTemac_GetOperatingSpeed returns the link speed in units of megabits + * per second. + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +u16 XLlTemac_GetOperatingSpeed(XLlTemac *InstancePtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_NONVOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & + XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_GetOperatingSpeed\n"); + switch (XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_EMMC_OFFSET) & + XTE_EMMC_LINKSPEED_MASK) { + case XTE_EMMC_LINKSPD_1000: + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_GetOperatingSpeed: returning 1000\n"); + return (1000); + + case XTE_EMMC_LINKSPD_100: + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_GetOperatingSpeed: returning 100\n"); + return (100); + + case XTE_EMMC_LINKSPD_10: + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_GetOperatingSpeed: returning 10\n"); + return (10); + + default: + return (0); + } +} + + +/*****************************************************************************/ +/** + * XLlTemac_SetOperatingSpeed sets the current operating link speed. For any + * traffic to be passed, this speed must match the current MII/GMII/SGMII/RGMII + * link speed. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param Speed is the speed to set in units of Mbps. Valid values are 10, 100, + * or 1000. XLlTemac_SetOperatingSpeed ignores invalid values. + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +void XLlTemac_SetOperatingSpeed(XLlTemac *InstancePtr, u16 Speed) +{ + u32 EmmcReg; + + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + XASSERT_VOID((Speed == 10) || (Speed == 100) || (Speed == 1000)); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_VOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & + XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_SetOperatingSpeed\n"); + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_SetOperatingSpeed: setting speed to: %d (0x%0x)\n", + Speed, Speed); + /* Get the current contents of the EMAC config register and zero out + * speed bits + */ + EmmcReg = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_EMMC_OFFSET) & + ~XTE_EMMC_LINKSPEED_MASK; + + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_SetOperatingSpeed: current speed: 0x%0x\n", + EmmcReg); + switch (Speed) { + case 10: + break; + + case 100: + EmmcReg |= XTE_EMMC_LINKSPD_100; + break; + + case 1000: + EmmcReg |= XTE_EMMC_LINKSPD_1000; + break; + + default: + return; + } + + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_SetOperatingSpeed: new speed: 0x%0x\n", EmmcReg); + /* Set register and return */ + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_EMMC_OFFSET, EmmcReg); + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_SetOperatingSpeed: done\n"); +} + +/*****************************************************************************/ +/** + * XLlTemac_PhySetMdioDivisor sets the MDIO clock divisor in the TEMAC channel, + * specified by InstancePtr to the value, Divisor. This function + * must be called once after each reset prior to accessing MII PHY registers. + * + * From the Virtex-4 Embedded Tri-Mode Ethernet MAC User's Guide, the + * following equation governs the MDIO clock to the PHY: + * + *
+ *              f[HOSTCLK]
+ *   f[MDC] = -----------------
+ *            (1 + Divisor) * 2
+ * 
+ * + * where f[HOSTCLK] is the bus clock frequency in MHz, and f[MDC] is the + * MDIO clock frequency in MHz to the PHY. Typically, f[MDC] should not + * exceed 2.5 MHz. Some PHYs can tolerate faster speeds which means faster + * access. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param Divisor is the divisor value to set within the range of 0 to + * XTE_MC_CLK_DVD_MAX. + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +void XLlTemac_PhySetMdioDivisor(XLlTemac *InstancePtr, u8 Divisor) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY) + XASSERT_VOID(Divisor <= XTE_MC_CLOCK_DIVIDE_MAX); + + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_VOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & + XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_PhySetMdioDivisor\n"); + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_MC_OFFSET, + (u32) Divisor | XTE_MC_MDIOEN_MASK); +} + +/*****************************************************************************/ +/* + * XLlTemac_PhyRead reads the specified PHY register, RegiseterNum on the + * PHY specified by PhyAddress into PhyDataPtr. This Ethernet + * driver does not require the device to be stopped before reading from the PHY. + * It is the responsibility of the calling code to stop the device if it is + * deemed necessary. + * + * Note that the TEMAC hardware provides the ability to talk to a PHY that + * adheres to the Media Independent Interface (MII) as defined in the IEEE 802.3 + * standard. + * + * It is important that calling code set up the MDIO clock with + * XLlTemac_PhySetMdioDivisor() prior to accessing the PHY with this function. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param PhyAddress is the address of the PHY to be written (multiple + * PHYs supported). + * @param RegisterNum is the register number, 0-31, of the specific PHY register + * to write. + * @param PhyDataPtr is a reference to the location where the 16-bit result + * value is stored. + * + * @return N/A + * + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr.

+ * + * There is the possibility that this function will not return if the hardware + * is broken (i.e., it never sets the status bit indicating that the write is + * done). If this is of concern, the calling code should provide a mechanism + * suitable for recovery. + * + ******************************************************************************/ +void XLlTemac_PhyRead(XLlTemac *InstancePtr, u32 PhyAddress, + u32 RegisterNum, u16 *PhyDataPtr) +{ + u32 MiiReg; + u32 Rdy; + u32 Ie; + u32 Tis; + + XASSERT_VOID(InstancePtr != NULL); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_VOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & + XTE_RDY_HARD_ACS_RDY_MASK); + + + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_PhyRead: BaseAddress: 0x%08x\n", + InstancePtr->Config.BaseAddress); + /* + * XLlTemac_PhyRead saves the state of the IE register so that it can + * clear the HardAcsCmplt bit and later restore the state of the IE + * register. Since XLlTemac_PhyRead will poll for the status already, the + * HardAcsCmplt bit is cleared in the IE register so that the + * application code above doesn't also receive the interrupt. + */ + Ie = XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, XTE_IE_OFFSET); + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, XTE_IE_OFFSET, + Ie & ~XTE_INT_HARDACSCMPLT_MASK); + + /* + * This is a double indirect mechanism. We indirectly write the + * PHYAD and REGAD so we can read the PHY register back out in + * the LSW register. + * + * In this case, the method of reading the data is a little unusual. + * Normally to write to a TEMAC register, one would set the WEN bit + * in the CTL register so that the values of the LSW will be written. + * + * In this case, the WEN bit is not set, and the PHYAD and REGAD + * values in the LSW will still get sent to the PHY before actually + * reading the result in the LSW. + * + * What needs to be done, is the following: + * 1) Write lsw reg with the phyad, and the regad + * 2) write the ctl reg with the miimai value (BUT WEN bit set to 0!!!) + * 3) poll the ready bit + * 4) get the value out of lsw + */ + MiiReg = RegisterNum & XTE_MIIM_REGAD_MASK; + MiiReg |= ((PhyAddress << XTE_MIIM_PHYAD_SHIFT) & XTE_MIIM_PHYAD_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_PhyRead: Mii Reg: 0x%0x; Value written: 0x%0x\n", + RegisterNum, MiiReg); + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, XTE_LSW_OFFSET, + MiiReg); + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, XTE_CTL_OFFSET, + XTE_MIIMAI_OFFSET); + + /* + * Wait here polling, until the value is ready to be read. + */ + do { + Rdy = XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET); + } while (!(Rdy & XTE_RSE_MIIM_RR_MASK)); + + /* Read data */ + *PhyDataPtr = XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_LSW_OFFSET); + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_PhyRead: Value retrieved: 0x%0x\n", *PhyDataPtr); + + /* + * Clear MII status bits. The TIS register in the hard TEMAC doesn't + * use the 'write a 1 to clear' method, so we need to read the TIS + * register, clear the MIIM RST bit, and then write it back out. + */ + Tis = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_TIS_OFFSET); + Tis &= ~XTE_RSE_MIIM_RR_MASK; + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_TIS_OFFSET, Tis); + + /* + * restore the state of the IE reg + */ + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, XTE_IE_OFFSET, Ie); +} + + +/*****************************************************************************/ +/* + * XLlTemac_PhyWrite writes PhyData to the specified PHY register, + * RegiseterNum on the PHY specified by PhyAddress. This Ethernet + * driver does not require the device to be stopped before writing to the PHY. + * It is the responsibility of the calling code to stop the device if it is + * deemed necessary. + * + * Note that the TEMAC hardware provides the ability to talk to a PHY that + * adheres to the Media Independent Interface (MII) as defined in the IEEE 802.3 + * standard. + * + * It is important that calling code set up the MDIO clock with + * XLlTemac_PhySetMdioDivisor() prior to accessing the PHY with this function. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param PhyAddress is the address of the PHY to be written (multiple + * PHYs supported). + * @param RegisterNum is the register number, 0-31, of the specific PHY register + * to write. + * @param PhyData is the 16-bit value that will be written to the register. + * + * @return N/A + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr.

+ * + * There is the possibility that this function will not return if the hardware + * is broken (i.e., it never sets the status bit indicating that the write is + * done). If this is of concern, the calling code should provide a mechanism + * suitable for recovery. + * + ******************************************************************************/ +void XLlTemac_PhyWrite(XLlTemac *InstancePtr, u32 PhyAddress, + u32 RegisterNum, u16 PhyData) +{ + u32 MiiReg; + u32 Rdy; + u32 Ie; + u32 Tis; + + XASSERT_VOID(InstancePtr != NULL); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_VOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & + XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_PhyWrite\n"); + /* + * XLlTemac_PhyWrite saves the state of the IE register so that it can + * clear the HardAcsCmplt bit and later restore the state of the IE + * register. Since XLlTemac_PhyWrite will poll for the status already, the + * HardAcsCmplt bit is cleared in the IE register so that the + * application code above doesn't also receive the interrupt. + */ + Ie = XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, XTE_IE_OFFSET); + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, XTE_IE_OFFSET, + Ie & ~XTE_INT_HARDACSCMPLT_MASK); + + /* + * This is a double indirect mechanism. We indirectly write the + * PhyData to the MIIMWD register, and then indirectly write PHYAD and + * REGAD so the value in MIIMWD will get written to the PHY. + */ + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_MIIMWD_OFFSET, PhyData); + + MiiReg = RegisterNum & XTE_MIIM_REGAD_MASK; + MiiReg |= ((PhyAddress << XTE_MIIM_PHYAD_SHIFT) & XTE_MIIM_PHYAD_MASK); + + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_MIIMAI_OFFSET, MiiReg); + + /* + * Wait here polling, until the value is ready to be read. + */ + do { + Rdy = XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET); + } while (!(Rdy & XTE_RSE_MIIM_WR_MASK)); + + /* + * Clear MII status bits. The TIS register in the hard TEMAC doesn't + * use the 'write a 1 to clear' method, so we need to read the TIS + * register, clear the MIIM WST bit, and then write it back out. + */ + Tis = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_TIS_OFFSET); + Tis &= XTE_RSE_MIIM_WR_MASK; + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_TIS_OFFSET, Tis); + + /* + * restore the state of the IE reg + */ + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, XTE_IE_OFFSET, Ie); +} diff --git a/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac.h b/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac.h new file mode 100644 index 0000000..0bcca30 --- /dev/null +++ b/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac.h @@ -0,0 +1,785 @@ +/* $Id: */ +/****************************************************************************** +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" +* AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND +* SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, +* OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, +* APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION +* THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, +* AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE +* FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY +* WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE +* IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR +* REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF +* INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE. +* +* (c) Copyright 2005-2008 Xilinx Inc. +* All rights reserved. +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +* +******************************************************************************/ +/*****************************************************************************/ +/** + * + * @file xlltemac.h + * + * The Xilinx Tri-Mode Ethernet driver component. This driver supports the + * Virtex-5(TM) and Virtex-4(TM) 10/100/1000 MAC (TEMAC). + * + * For a full description of TEMAC features, please see the hardware spec. This driver + * supports the following features: + * - Memory mapped access to host interface registers + * - Virtual memory support + * - Unicast, broadcast, and multicast receive address filtering + * - Full duplex operation (half duplex not supported) + * - Automatic source address insertion or overwrite (programmable) + * - Automatic PAD & FCS insertion and stripping (programmable) + * - Flow control + * - VLAN frame support + * - Pause frame support + * - Jumbo frame support + * - Checksum offload + * + *

Driver Description

+ * + * The device driver enables higher layer software (e.g., an application) to + * configure a TEMAC channel. It is intended that this driver be used in + * cooperation with another driver (FIFO or DMA) for data communication. This + * device driver can support multiple devices even when those devices have + * significantly different configurations. + * + *

Initialization & Configuration

+ * + * The XLlTemac_Config structure can be used by the driver to configure itself. + * This configuration structure is typically created by the tool-chain based on + * hardware build properties, although, other methods are allowed and currently + * used in some systems. + * + * To support multiple runtime loading and initialization strategies employed + * by various operating systems, the driver instance can be initialized using + * the XLlTemac_CfgInitialze() routine. + * + *

Interrupts and Asynchronous Callbacks

+ * + * The driver has no dependencies on the interrupt controller. It provides + * no interrupt handlers. The application/OS software should set up its own + * interrupt handlers if required. + * + *

Device Reset

+ * + * When a TEMAC channel is connected up to a FIFO or DMA core in hardware, + * errors may be reported on one of those cores (FIFO or DMA) such that it can + * be determined that the TEMAC channel needs to be reset. If a reset is + * performed, the calling code should also reconfigure and reapply the proper + * settings in the TEMAC channel. + * + * When a TEMAC channel reset is required, XLlTemac_Reset() should be utilized. + * + *

Virtual Memory

+ * + * This driver may be used in systems with virtual memory support by passing + * the appropriate value for the EffectiveAddress parameter to the + * XLlTemac_CfgInitialize() routine. + * + *

Transfering Data

+ * + * The TEMAC core by itself is not cabable of transmitting or receiving data in + * any meaninful way. Instead one or both TEMAC channels need to be connected + * to a FIFO or DMA core in hardware. + * + * This TEMAC driver is modeled in a similar fashion where the application code + * or O/S adapter driver needs to make use of a separte FIFO or DMA driver in + * connection with this driver to establish meaningful communication over + * ethernet. + * + *

Checksum Offloading

+ * + * If configured, the device can compute a 16-bit checksum from frame data. In + * most circumstances this can lead to a substantial gain in throughput. + * + * The checksum offload settings for each frame sent or recieved are + * transmitted through the LocalLink interface in hardware. What this means is + * that the checksum offload feature is indirectly controlled in the TEMAC + * channel through the driver for the FIFO or DMA core connected to the TEMAC + * channel. + * + * Refer to the documentation for the FIFO or DMA driver used for data + * communication on how to set the values for the relevant LocalLink header + * words. + * + * Since this hardware implementation is general purpose in nature system software must + * perform pre and post frame processing to obtain the desired results for the + * types of packets being transferred. Most of the time this will be TCP/IP + * traffic. + * + * TCP/IP and UDP/IP frames contain separate checksums for the IP header and + * UDP/TCP header+data. With this hardware implementation, the IP header checksum + * cannot be offloaded. Many stacks that support offloading will compute the IP + * header if required and use hardware to compute the UDP/TCP header+data checksum. + * There are other complications concerning the IP pseudo header that must be + * taken into consideration. Readers should consult a TCP/IP design reference + * for more details. + * + * There are certain device options that will affect the checksum calculation + * performed by hardware for Tx: + * + * - FCS insertion disabled (XTE_FCS_INSERT_OPTION): software is required to + * calculate and insert the FCS value at the end of the frame, but the + * checksum must be known ahead of time prior to calculating the FCS. + * Therefore checksum offloading cannot be used in this situation. + * + * And for Rx: + * + * - FCS/PAD stripping disabled (XTE_FCS_STRIP_OPTION): The 4 byte FCS at the + * end of frame will be included in the hardware calculated checksum. software must + * subtract out this data. + * + * - FCS/PAD stripping disabled (XTE_FCS_STRIP_OPTION): For frames smaller + * than 64 bytes, padding will be included in the hardware calculated checksum. + * software must subtract out this data. It may be better to allow the TCP/IP + * stack verify checksums for this type of packet. + * + * - VLAN enabled (XTE_VLAN_OPTION): The 4 extra bytes in the Ethernet header + * affect the hardware calculated checksum. software must subtract out the 1st two + * 16-bit words starting at the 15th byte. + * + *

Transmit Checksum Offloading

+ * + * For transmit, the software can specify where in the frame the checksum + * calculation is to start, where the result should be inserted, and a seed + * value. The checksum is calculated from the start point through the end of + * frame. + * + * The checsum offloading settings are sent in the transmit LocalLink header + * words. The relevant LocalLink header words are described in brief below. + * Refer to the XPS_LL_TEMAC v1.00a hardware specification for more details. + * + *

LocalLink header word 3:

+ *
+ *   Bits    31 (MSB): Transmit Checksum Enable: 1 - enabled, 0 - disabled
+ *   Bits  0-30 (LSB): Reserved
+ *   
+ * + *

LocalLink header word 4:

+ *
+ *   Bits 16-31 (MSB): Transmit Checksum Insertion Point: Frame offset where the
+ *                     computed checksum value is stored, which should be in the
+ *                     TCP or UDP header
+ *   Bits  0-15 (LSB): Transmit Checksum Calculation Starting Point: Offset
+ *                     in the frame where checksum calculation should begin
+ *   
+ * + *

LocalLink header word 5:

+ *
+ *   Bits 16-31 (MSB): Transmit Checksum Calculation Initial Value: Checksum
+ *                     seed value
+ *   Bits  0-15 (LSB): Reserved
+ *   
+ * + *

Receive Checksum Offloading

+ * + * For Receive, the 15th byte to end of frame is checksummed. This range of + * bytes is the entire Ethernet payload (for non-VLAN frames). + * + * The checsum offloading information is sent in the receive LocalLink header + * words. The relevant LocalLink header words are described in brief below. + * Refer to the XPS_LL_TEMAC v1.00a hardware specification for more details. + * + *

LocalLink header word 6:

+ *
+ *   Bits 16-31 (MSB): Receive Raw Checksum: Computed checksum value
+ *   Bits  0-15 (LSB): Reserved
+ *   
+ * + *

PHY Communication

+ * + * Prior to PHY access, the MDIO clock must be setup. This driver will set a + * safe default that should work with PLB bus speeds of up to 150 MHz and keep + * the MDIO clock below 2.5 MHz. If the user wishes faster access to the PHY + * then the clock divisor can be set to a different value (see + * XLlTemac_PhySetMdioDivisor()). + * + * MII register access is performed through the functions XLlTemac_PhyRead() and + * XLlTemac_PhyWrite(). + * + *

Link Sync

+ * + * When the device is used in a multispeed environment, the link speed must be + * explicitly set using XLlTemac_SetOperatingSpeed() and must match the speed the + * PHY has negotiated. If the speeds are mismatched, then the MAC will not pass + * traffic. + * + * The application/OS software may use the AutoNegotiation interrupt to be + * notified when the PHY has completed auto-negotiation. + * + *

Asserts

+ * + * Asserts are used within all Xilinx drivers to enforce constraints on argument + * values. Asserts can be turned off on a system-wide basis by defining, at + * compile time, the NDEBUG identifier. By default, asserts are turned on and it + * is recommended that users leave asserts on during development. For deployment + * use -DNDEBUG compiler switch to remove assert code. + * + *

Driver Errata

+ * + * - A dropped receive frame indication may be reported by the driver after + * calling XLlTemac_Stop() followed by XLlTemac_Start(). This can occur if a + * frame is arriving when stop is called. + * - On Rx with checksum offloading enabled and FCS/PAD stripping disabled, + * FCS and PAD data will be included in the checksum result. + * - On Tx with checksum offloading enabled and auto FCS insertion disabled, + * the user calculated FCS will be included in the checksum result. + * + * @note + * + * Xilinx drivers are typically composed of two components, one is the driver + * and the other is the adapter. The driver is independent of OS and processor + * and is intended to be highly portable. The adapter is OS-specific and + * facilitates communication between the driver and an OS. + *

+ * This driver is intended to be RTOS and processor independent. Any needs for + * dynamic memory management, threads or thread mutual exclusion, or cache + * control must be satisfied by the layer above this driver. + * + *
+ * MODIFICATION HISTORY:
+ *
+ * Ver   Who  Date     Changes
+ * ----- ---- -------- -------------------------------------------------------
+ * 1.00a jvb  11/10/06 First release
+ * 1.00a rpm  06/08/07 Added interrupt IDs to config structure for convenience
+ * 
+ * + *****************************************************************************/ + +#ifndef XTEMAC_H /* prevent circular inclusions */ +#define XTEMAC_H /* by using protection macros */ + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************** Include Files *********************************/ + +#include "xenv.h" +#include "xbasic_types.h" +#include "xstatus.h" +#include "xlltemac_hw.h" + +/************************** Constant Definitions *****************************/ + +/* + * Device information + */ +#define XTE_DEVICE_NAME "xlltemac" +#define XTE_DEVICE_DESC "Xilinx Tri-speed 10/100/1000 MAC" + +/* LocalLink TYPE Enumerations */ +#define XPAR_LL_FIFO 1 +#define XPAR_LL_DMA 2 + +/** @name Configuration options + * + * The following are device configuration options. See the + * XLlTemac_SetOptions, XLlTemac_ClearOptions and + * XLlTemac_GetOptions routines for information on how to use options. + * + * The default state of the options are also noted below. + * + * @{ + */ + +#define XTE_PROMISC_OPTION 0x00000001 +/**< XTE_PROMISC_OPTION specifies the TEMAC channel to accept all incoming + * packets. + * This driver sets this option to disabled (cleared) by default. */ + +#define XTE_JUMBO_OPTION 0x00000002 +/**< XTE_JUMBO_OPTION specifies the TEMAC channel to accept jumbo frames + * for transmit and receive. + * This driver sets this option to disabled (cleared) by default. */ + +#define XTE_VLAN_OPTION 0x00000004 +/**< XTE_VLAN_OPTION specifies the TEMAC channel to enable VLAN support for + * transmit and receive. + * This driver sets this option to disabled (cleared) by default. */ + +#define XTE_FLOW_CONTROL_OPTION 0x00000008 +/**< XTE_FLOW_CONTROL_OPTION specifies the TEMAC channel to recognize + * received flow control frames. + * This driver sets this option to enabled (set) by default. */ + +#define XTE_FCS_STRIP_OPTION 0x00000010 +/**< XTE_FCS_STRIP_OPTION specifies the TEMAC channel to strip FCS and PAD + * from received frames. Note that PAD from VLAN frames is not stripped. + * This driver sets this option to enabled (set) by default. */ + +#define XTE_FCS_INSERT_OPTION 0x00000020 +/**< XTE_FCS_INSERT_OPTION specifies the TEMAC channel to generate the FCS + * field and add PAD automatically for outgoing frames. + * This driver sets this option to enabled (set) by default. */ + +#define XTE_LENTYPE_ERR_OPTION 0x00000040 +/**< XTE_LENTYPE_ERR_OPTION specifies the TEMAC channel to enable + * Length/Type error checking (mismatched type/length field) for received + * frames. + * This driver sets this option to enabled (set) by default. */ + +#define XTE_TRANSMITTER_ENABLE_OPTION 0x00000080 +/**< XTE_TRANSMITTER_ENABLE_OPTION specifies the TEMAC channel transmitter + * to be enabled. + * This driver sets this option to enabled (set) by default. */ + +#define XTE_RECEIVER_ENABLE_OPTION 0x00000100 +/**< XTE_RECEIVER_ENABLE_OPTION specifies the TEMAC channel receiver to be + * enabled. + * This driver sets this option to enabled (set) by default. */ + +#define XTE_BROADCAST_OPTION 0x00000200 +/**< XTE_BROADCAST_OPTION specifies the TEMAC channel to receive frames + * sent to the broadcast Ethernet address. + * This driver sets this option to enabled (set) by default. */ + +#define XTE_MULTICAST_OPTION 0x00000400 +/**< XTE_MULTICAST_OPTION specifies the TEMAC channel to receive frames + * sent to Ethernet addresses that are programmed into the Multicast Address + * Table (MAT). + * This driver sets this option to disabled (cleared) by default. */ + +#define XTE_DEFAULT_OPTIONS \ + (XTE_FLOW_CONTROL_OPTION | \ + XTE_BROADCAST_OPTION | \ + XTE_FCS_INSERT_OPTION | \ + XTE_FCS_STRIP_OPTION | \ + XTE_LENTYPE_ERR_OPTION | \ + XTE_TRANSMITTER_ENABLE_OPTION | \ + XTE_RECEIVER_ENABLE_OPTION) +/**< XTE_DEFAULT_OPTIONS specify the options set in XLlTemac_Reset() and + * XLlTemac_CfgInitialize() */ + +/*@}*/ + +/** @name Reset parameters + * + * These are used by function XLlTemac_Reset(). + * @{ + */ +#define XTE_RESET_HARD 1 +#define XTE_NORESET_HARD 0 +/*@}*/ + +#define XTE_MULTI_MAT_ENTRIES 4 /* Number of storable addresses in + the Multicast Address Table */ + +#define XTE_MDIO_DIV_DFT 29 /* Default MDIO clock divisor */ + +/* The next few constants help upper layers determine the size of memory + * pools used for Ethernet buffers and descriptor lists. + */ +#define XTE_MAC_ADDR_SIZE 6 /* MAC addresses are 6 bytes */ +#define XTE_MTU 1500 /* max MTU size of an Ethernet frame */ +#define XTE_JUMBO_MTU 8982 /* max MTU size of a jumbo Ethernet frame */ +#define XTE_HDR_SIZE 14 /* size of an Ethernet header */ +#define XTE_HDR_VLAN_SIZE 18 /* size of an Ethernet header with VLAN */ +#define XTE_TRL_SIZE 4 /* size of an Ethernet trailer (FCS) */ +#define XTE_MAX_FRAME_SIZE (XTE_MTU + XTE_HDR_SIZE + XTE_TRL_SIZE) +#define XTE_MAX_VLAN_FRAME_SIZE (XTE_MTU + XTE_HDR_VLAN_SIZE + XTE_TRL_SIZE) +#define XTE_MAX_JUMBO_FRAME_SIZE (XTE_JUMBO_MTU + XTE_HDR_SIZE + XTE_TRL_SIZE) + +/* Constant values returned by XLlTemac_mGetPhysicalInterface(). Note that these + * values match design parameters from the PLB_TEMAC spec + */ +#define XTE_PHY_TYPE_MII 0 +#define XTE_PHY_TYPE_GMII 1 +#define XTE_PHY_TYPE_RGMII_1_3 2 +#define XTE_PHY_TYPE_RGMII_2_0 3 +#define XTE_PHY_TYPE_SGMII 4 +#define XTE_PHY_TYPE_1000BASE_X 5 + +/**************************** Type Definitions *******************************/ + + +/** + * This typedef contains configuration information for a TEMAC channel. + * Each channel is treated as a separate device from the point of view of this + * driver. + */ +typedef struct { + /** u16 DeviceId; < DeviceId is the unique ID of the device */ + u32 BaseAddress;/**< BaseAddress is the physical base address of the + * channel's registers + */ + u8 TxCsum; /**< TxCsum indicates that the channel has checksum + * offload on the Tx channel or not. + */ + u8 RxCsum; /**< RxCsum indicates that the channel has checksum + * offload on the Rx channel or not. + */ + u8 PhyType; /**< PhyType indicates which type of PHY interface is + * used (MII, GMII, RGMII, ect. + */ + u8 TemacIntr; /**< TEMAC interrupt ID */ + + int LLDevType; /**< LLDevType is the type of device attached to the + * temac's local link interface. + */ + u32 LLDevBaseAddress; /**< LLDevBaseAddress is the base address of then + * device attached to the temac's local link + * interface. + */ + u8 LLFifoIntr; /**< LL FIFO interrupt ID (unused if DMA) */ + u8 LLDmaRxIntr; /**< LL DMA RX interrupt ID (unused if FIFO) */ + u8 LLDmaTxIntr; /**< LL DMA TX interrupt ID (unused if FIFO) */ + +} XLlTemac_Config; + + +/** + * struct XLlTemac is the type for TEMAC driver instance data. The calling code + * is required to use a unique instance of this structure for every TEMAC + * channel used in the system. Each channel is treated as a separate device + * from the point of view of this driver. A reference to a structure of this + * type is then passed to the driver API functions. + */ +typedef struct XLlTemac { + XLlTemac_Config Config; /* hardware configuration */ + u32 IsStarted; /* Device is currently started */ + u32 IsReady; /* Device is initialized and ready */ + u32 Options; /* Current options word */ + u32 Flags; /* Internal driver flags */ +} XLlTemac; + + +/***************** Macros (Inline Functions) Definitions *********************/ + +/*****************************************************************************/ +/** + * + * XLlTemac_IsStarted reports if the device is in the started or stopped state. To + * be in the started state, the calling code must have made a successful call to + * XLlTemac_Start. To be in the stopped state, XLlTemac_Stop or + * XLlTemac_CfgInitialize function must have been called. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * + * @return XLlTemac_IsStarted returns TRUE if the device has been started. + * Otherwise, XLlTemac_IsStarted returns FALSE. + * + * @note + * + * Signature: u32 XLlTemac_IsStarted(XLlTemac *InstancePtr) + * + ******************************************************************************/ +#define XLlTemac_IsStarted(InstancePtr) \ + (((InstancePtr)->IsStarted == XCOMPONENT_IS_STARTED) ? TRUE : FALSE) + +/*****************************************************************************/ +/** +* +* XLlTemac_IsDma reports if the device is currently connected to DMA. +* +* @param InstancePtr references the TEMAC channel on which to operate. +* +* @return XLlTemac_IsDma returns TRUE if the device is connected DMA. Otherwise, +* XLlTemac_IsDma returns FALSE. +* +* @note +* +* Signature: u32 XLlTemac_IsDma(XLlTemac *InstancePtr) +* +******************************************************************************/ +#define XLlTemac_IsDma(InstancePtr) \ + (((InstancePtr)->Config.LLDevType == XPAR_LL_DMA) ? TRUE: FALSE) + +/*****************************************************************************/ +/** +* +* XLlTemac_IsFifo reports if the device is currently connected to a fifo core. +* +* @param InstancePtr references the TEMAC channel on which to operate. +* +* @return XLlTemac_IsFifo returns TRUE if the device is connected to a fifo core. +* Otherwise, XLlTemac_IsFifo returns FALSE. +* +* @note +* +* Signature: u32 XLlTemac_IsFifo(XLlTemac *InstancePtr) +* +******************************************************************************/ +#define XLlTemac_IsFifo(InstancePtr) \ + (((InstancePtr)->Config.LLDevType == XPAR_LL_FIFO) ? TRUE: FALSE) + +/*****************************************************************************/ +/** +* +* XLlTemac_LlDevBaseAddress reports the base address of the core connected to +* the TEMAC's local link interface. +* +* @param InstancePtr references the TEMAC channel on which to operate. +* +* @return XLlTemac_IsFifo returns the base address of the core connected to +* the TEMAC's local link interface. +* +* @note +* +* Signature: u32 XLlTemac_LlDevBaseAddress(XLlTemac *InstancePtr) +* +******************************************************************************/ +#define XLlTemac_LlDevBaseAddress(InstancePtr) \ + ((InstancePtr)->Config.LLDevBaseAddress) + +/*****************************************************************************/ +/** + * + * XLlTemac_IsRecvFrameDropped determines if the device thinks it has dropped a + * receive frame. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * + * @return XLlTemac_IsRecvFrameDropped returns TRUE if the device interrupt + * status register reports that a frame has been dropped. Otherwise, + * XLlTemac_IsRecvFrameDropped returns FALSE. + * + * @note + * + * Signature: u32 XLlTemac_IsRecvFrameDropped(XLlTemac *InstancePtr) + * + ******************************************************************************/ +#define XLlTemac_IsRecvFrameDropped(InstancePtr) \ + ((XLlTemac_ReadReg((InstancePtr)->Config.BaseAddress, XTE_IS_OFFSET) \ + & XTE_INT_RXRJECT_MASK) ? TRUE : FALSE) + +/*****************************************************************************/ +/** + * + * XLlTemac_IsRxCsum determines if the device is configured with checksum + * offloading on the receive channel. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * + * @return XLlTemac_IsRxCsum returns TRUE if the device is configured with + * checksum offloading on the receive channel. Otherwise, + * XLlTemac_IsRxCsum returns FALSE. + * + * @note + * + * Signature: u32 XLlTemac_IsRxCsum(XLlTemac *InstancePtr) + * + ******************************************************************************/ +#define XLlTemac_IsRxCsum(InstancePtr) (((InstancePtr)->Config.RxCsum) ? \ + TRUE : FALSE) + +/*****************************************************************************/ +/** + * + * XLlTemac_IsTxCsum determines if the device is configured with checksum + * offloading on the transmit channel. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * + * @return XLlTemac_IsTxCsum returns TRUE if the device is configured with + * checksum offloading on the transmit channel. Otherwise, + * XLlTemac_IsTxCsum returns FALSE. + * + * @note + * + * Signature: u32 XLlTemac_IsTxCsum(XLlTemac *InstancePtr) + * + ******************************************************************************/ +#define XLlTemac_IsTxCsum(InstancePtr) (((InstancePtr)->Config.TxCsum) ? \ + TRUE : FALSE) + +/*****************************************************************************/ +/** + * + * XLlTemac_GetPhysicalInterface returns the type of PHY interface being used by + * the given instance, specified by InstancePtr. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * + * @return XLlTemac_GetPhysicalInterface returns one of XTE_PHY_TYPE_ where + * is MII, GMII, RGMII_1_3, RGMII_2_0, SGMII, or 1000BASE_X (defined in + * xlltemac.h). + * + * @note + * + * Signature: int XLlTemac_GetPhysicalInterface(XLlTemac *InstancePtr) + * + ******************************************************************************/ +#define XLlTemac_GetPhysicalInterface(InstancePtr) \ + ((InstancePtr)->Config.PhyType) + +/****************************************************************************/ +/** +* +* XLlTemac_Status returns a bit mask of the interrupt status register (ISR). +* XLlTemac_Status can be used to query the status without having to have +* interrupts enabled. +* +* @param InstancePtr references the TEMAC channel on which to operate. +* +* @return XLlTemac_IntStatus returns a bit mask of the status conditions. +* The mask will be a set of bitwise or'd values from the +* XTE_INT_*_MASK preprocessor symbols. +* +* @note +* C-style signature: +* u32 XLlTemac_IntStatus(XLlTemac *InstancePtr) +* +*****************************************************************************/ +#define XLlTemac_Status(InstancePtr) \ + XLlTemac_ReadReg((InstancePtr)->Config.BaseAddress, XTE_IS_OFFSET) + +/****************************************************************************/ +/** +* +* XLlTemac_IntEnable enables the interrupts specified in Mask. The +* corresponding interrupt for each bit set to 1 in Mask, will be +* enabled. +* +* @param InstancePtr references the TEMAC channel on which to operate. +* +* @param Mask contains a bit mask of the interrupts to enable. The mask +* can be formed using a set of bitwise or'd values from the +* XTE_INT_*_MASK preprocessor symbols. +* +* @return N/A +* +* @note +* C-style signature: +* void XLlTemac_IntEnable(XLlTemac *InstancePtr, u32 Mask) +* +*****************************************************************************/ +#define XLlTemac_IntEnable(InstancePtr, Mask) \ + XLlTemac_WriteReg((InstancePtr)->Config.BaseAddress, XTE_IE_OFFSET, \ + XLlTemac_ReadReg((InstancePtr)->Config.BaseAddress, \ + XTE_IE_OFFSET) | ((Mask) & XTE_INT_ALL_MASK)); \ + +/****************************************************************************/ +/** +* +* XLlTemac_IntDisable disables the interrupts specified in Mask. The +* corresponding interrupt for each bit set to 1 in Mask, will be +* disabled. In other words, XLlTemac_IntDisable uses the "set a bit to clear it" +* scheme. +* +* @param InstancePtr references the TEMAC channel on which to operate. +* +* @param Mask contains a bit mask of the interrupts to disable. The mask +* can be formed using a set of bitwise or'd values from the +* XTE_INT_*_MASK preprocessor symbols. +* +* @return N/A +* +* @note +* C-style signature: +* void XLlTemac_IntDisable(XLlTemac *InstancePtr, u32 Mask) +* +*****************************************************************************/ +#define XLlTemac_IntDisable(InstancePtr, Mask) \ + XLlTemac_WriteReg((InstancePtr)->Config.BaseAddress, XTE_IE_OFFSET, \ + XLlTemac_ReadReg((InstancePtr)->Config.BaseAddress, \ + XTE_IE_OFFSET) & ~((Mask) & XTE_INT_ALL_MASK)); \ + +/****************************************************************************/ +/** +* +* XLlTemac_IntPending returns a bit mask of the pending interrupts. Each bit +* set to 1 in the return value represents a pending interrupt. +* +* @param InstancePtr references the TEMAC channel on which to operate. +* +* @return XLlTemac_IntPending returns a bit mask of the interrupts that are +* pending. The mask will be a set of bitwise or'd values from the +* XTE_INT_*_MASK preprocessor symbols. +* +* @note +* C-style signature: +* u32 XLlTemac_IntPending(XLlTemac *InstancePtr) +* +*****************************************************************************/ +#define XLlTemac_IntPending(InstancePtr) \ + XLlTemac_ReadReg((InstancePtr)->Config.BaseAddress, XTE_IP_OFFSET) + +/****************************************************************************/ +/** +* +* XLlTemac_IntClear clears pending interrupts specified in Mask. +* The corresponding pending interrupt for each bit set to 1 in Mask, +* will be cleared. In other words, XLlTemac_IntClear uses the "set a bit to +* clear it" scheme. +* +* @param InstancePtr references the TEMAC channel on which to operate. +* +* @param Mask contains a bit mask of the pending interrupts to clear. The +* mask can be formed using a set of bitwise or'd values from the +* XTE_INT_*_MASK preprocessor symbols. +* +* @note +* C-style signature: +* void XLlTemac_IntClear(XLlTemac *InstancePtr, u32 Mask) +* +*****************************************************************************/ +#define XLlTemac_IntClear(InstancePtr, Mask) \ + XLlTemac_WriteReg((InstancePtr)->Config.BaseAddress, XTE_IS_OFFSET, \ + ((Mask) & XTE_INT_ALL_MASK)) + +/************************** Function Prototypes ******************************/ + +/* + * Initialization functions in xlltemac.c + */ +int XLlTemac_CfgInitialize(XLlTemac *InstancePtr, XLlTemac_Config *CfgPtr, + u32 VirtualAddress); +void XLlTemac_Start(XLlTemac *InstancePtr); +void XLlTemac_Stop(XLlTemac *InstancePtr); +void XLlTemac_Reset(XLlTemac *InstancePtr, int HardCoreAction); + +/* + * Initialization functions in xlltemac_sinit.c + */ +XLlTemac_Config *XLlTemac_LookupConfig(u16 DeviceId); + +/* + * MAC configuration/control functions in xlltemac_control.c + */ +int XLlTemac_SetOptions(XLlTemac *InstancePtr, u32 Options); +int XLlTemac_ClearOptions(XLlTemac *InstancePtr, u32 Options); +u32 XLlTemac_GetOptions(XLlTemac *InstancePtr); + +int XLlTemac_SetMacAddress(XLlTemac *InstancePtr, void *AddressPtr); +void XLlTemac_GetMacAddress(XLlTemac *InstancePtr, void *AddressPtr); + +int XLlTemac_SetMacPauseAddress(XLlTemac *InstancePtr, void *AddressPtr); +void XLlTemac_GetMacPauseAddress(XLlTemac *InstancePtr, void *AddressPtr); +int XLlTemac_SendPausePacket(XLlTemac *InstancePtr, u16 PauseValue); + +int XLlTemac_GetSgmiiStatus(XLlTemac *InstancePtr, u16 *SpeedPtr); +int XLlTemac_GetRgmiiStatus(XLlTemac *InstancePtr, u16 *SpeedPtr, + int *IsFullDuplexPtr, int *IsLinkUpPtr); +u16 XLlTemac_GetOperatingSpeed(XLlTemac *InstancePtr); +void XLlTemac_SetOperatingSpeed(XLlTemac *InstancePtr, u16 Speed); + +void XLlTemac_PhySetMdioDivisor(XLlTemac *InstancePtr, u8 Divisor); +void XLlTemac_PhyRead(XLlTemac *InstancePtr, u32 PhyAddress, u32 RegisterNum, + u16 *PhyDataPtr); +void XLlTemac_PhyWrite(XLlTemac *InstancePtr, u32 PhyAddress, u32 RegisterNum, + u16 PhyData); +int XLlTemac_MulticastAdd(XLlTemac *InstancePtr, void *AddressPtr, int Entry); +void XLlTemac_MulticastGet(XLlTemac *InstancePtr, void *AddressPtr, int Entry); +int XLlTemac_MulticastClear(XLlTemac *InstancePtr, int Entry); + +#ifdef __cplusplus +} +#endif + +#endif /* end of protection macro */ diff --git a/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac_control.c b/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac_control.c new file mode 100644 index 0000000..436c761 --- /dev/null +++ b/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac_control.c @@ -0,0 +1,679 @@ +/* $Id: */ +/****************************************************************************** +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" +* AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND +* SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, +* OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, +* APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION +* THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, +* AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE +* FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY +* WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE +* IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR +* REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF +* INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE. +* +* (c) Copyright 2005-2008 Xilinx Inc. +* All rights reserved. +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +* +******************************************************************************/ +/*****************************************************************************/ +/** + * + * @file xlltemac_control.c + * + * Functions in this file implement general purpose command and control related + * functionality. See xlltemac.h for a detailed description of the driver. + * + *
+ * MODIFICATION HISTORY:
+ *
+ * Ver   Who  Date     Changes
+ * ----- ---- -------- -------------------------------------------------------
+ * 1.00a jvb  11/10/06 First release
+ * 
+ *****************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xlltemac.h" + +/************************** Constant Definitions *****************************/ + + +/**************************** Type Definitions *******************************/ + + +/***************** Macros (Inline Functions) Definitions *********************/ + + +/************************** Function Prototypes ******************************/ + + +/************************** Variable Definitions *****************************/ + + +/*****************************************************************************/ +/** + * in the TEMAC channel's multicast filter list. + * + * XLlTemac_MulticastAdd adds the Ethernet address, AddressPtr to the + * TEMAC channel's multicast filter list, at list index Entry. The + * address referenced by AddressPtr may be of any unicast, multicast, or + * broadcast address form. The harware for the TEMAC channel can hold up to + * XTE_MULTI_MAT_ENTRIES addresses in this filter list.

+ * + * The device must be stopped to use this function.

+ * + * Once an Ethernet address is programmed, the TEMAC channel will begin + * receiving data sent from that address. The TEMAC hardware does not have a + * control bit to disable multicast filtering. The only way to prevent the + * TEMAC channel from receiving messages from an Ethernet address in the + * Multicast Address Table (MAT) is to clear it with XLlTemac_MulticastClear(). + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param AddressPtr is a pointer to the 6-byte Ethernet address to set. The + * previous address at the location Entry (if any) is overwritten + * with the value at AddressPtr. + * @param Entry is the hardware storage location to program this address and + * must be between 0..XTE_MULTI_MAT_ENTRIES-1. + * + * @return On successful completion, XLlTemac_MulticastAdd returns XST_SUCCESS. + * Otherwise, if the TEMAC channel is not stopped, XLlTemac_MulticastAdd + * returns XST_DEVICE_IS_STARTED. + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +int XLlTemac_MulticastAdd(XLlTemac *InstancePtr, void *AddressPtr, int Entry) +{ + u32 Maw0Reg; + u32 Maw1Reg; + u8 *Aptr = (u8 *) AddressPtr; + u32 Rdy; + int MaxWait = 100; + u32 BaseAddress = InstancePtr->Config.BaseAddress; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + XASSERT_NONVOID(AddressPtr != NULL); + XASSERT_NONVOID(Entry < XTE_MULTI_MAT_ENTRIES); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_NONVOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_MulticastAdd\n"); + + /* The device must be stopped before clearing the multicast hash table */ + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_MulticastAdd: returning DEVICE_IS_STARTED\n"); + + return (XST_DEVICE_IS_STARTED); + } + + /* Set MAC bits [31:0] */ + Maw0Reg = Aptr[0]; + Maw0Reg |= Aptr[1] << 8; + Maw0Reg |= Aptr[2] << 16; + Maw0Reg |= Aptr[3] << 24; + + /* Set MAC bits [47:32] */ + Maw1Reg = Aptr[4]; + Maw1Reg |= Aptr[5] << 8; + + /* Add in MAT address */ + Maw1Reg |= (Entry << XTE_MAW1_MATADDR_SHIFT_MASK); + + /* Program HW */ + xdbg_printf(XDBG_DEBUG_GENERAL, "Setting MAT entry: %d\n", Entry); + XLlTemac_WriteReg(BaseAddress, XTE_LSW_OFFSET, Maw0Reg); + XLlTemac_WriteReg(BaseAddress, XTE_CTL_OFFSET, + XTE_MAW0_OFFSET | XTE_CTL_WEN_MASK); + Rdy = XLlTemac_ReadReg(BaseAddress, XTE_RDY_OFFSET); + while (MaxWait && (!(Rdy & XTE_RDY_HARD_ACS_RDY_MASK))) { + Rdy = XLlTemac_ReadReg(BaseAddress, XTE_RDY_OFFSET); + xdbg_stmnt( + if (MaxWait == 100) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "RDY reg not initially ready\n"); + } + ); + MaxWait--; + xdbg_stmnt( + if (MaxWait == 0) { + xdbg_printf (XDBG_DEBUG_GENERAL, + "RDY reg never showed ready\n"); + } + ) + } + XLlTemac_WriteReg(BaseAddress, XTE_LSW_OFFSET, + Maw1Reg); + XLlTemac_WriteReg(BaseAddress, XTE_CTL_OFFSET, + XTE_MAW1_OFFSET | XTE_CTL_WEN_MASK); + Rdy = XLlTemac_ReadReg(BaseAddress, XTE_RDY_OFFSET); + while (MaxWait && (!(Rdy & XTE_RDY_HARD_ACS_RDY_MASK))) { + Rdy = XLlTemac_ReadReg(BaseAddress, XTE_RDY_OFFSET); + xdbg_stmnt( + if (MaxWait == 100) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "RDY reg not initially ready\n"); + } + ); + MaxWait--; + xdbg_stmnt( + if (MaxWait == 0) { + xdbg_printf (XDBG_DEBUG_GENERAL, + "RDY reg never showed ready\n"); + } + ) + } + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_MulticastAdd: returning SUCCESS\n"); + + return (XST_SUCCESS); +} + + +/*****************************************************************************/ +/** + * XLlTemac_MulticastGet gets the Ethernet address stored at index Entry + * in the TEMAC channel's multicast filter list.

+ * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param AddressPtr references the memory buffer to store the retrieved + * Ethernet address. This memory buffer must be at least 6 bytes in + * length. + * @param Entry is the hardware storage location from which to retrieve the + * address and must be between 0..XTE_MULTI_MAT_ENTRIES-1. + * + * @return N/A + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +void XLlTemac_MulticastGet(XLlTemac *InstancePtr, void *AddressPtr, int Entry) +{ + u32 Maw0Reg; + u32 Maw1Reg; + u8 *Aptr = (u8 *) AddressPtr; + u32 Rdy; + int MaxWait = 100; + u32 BaseAddress = InstancePtr->Config.BaseAddress; + + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + XASSERT_VOID(Entry < XTE_MULTI_MAT_ENTRIES); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_VOID(XLlTemac_ReadReg(BaseAddress, XTE_RDY_OFFSET) & + XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_MulticastGet\n"); + + /* + * Tell HW to provide address stored in given entry. + * In this case, the Access is a little weird, becuase we need to + * write the LSW register first, then initiate a write operation, + * even though it's a read operation. + */ + xdbg_printf(XDBG_DEBUG_GENERAL, "Getting MAT entry: %d\n", Entry); + XLlTemac_WriteReg(BaseAddress, XTE_LSW_OFFSET, + Entry << XTE_MAW1_MATADDR_SHIFT_MASK | XTE_MAW1_RNW_MASK); + XLlTemac_WriteReg(BaseAddress, XTE_CTL_OFFSET, + XTE_MAW1_OFFSET | XTE_CTL_WEN_MASK); + Rdy = XLlTemac_ReadReg(BaseAddress, XTE_RDY_OFFSET); + while (MaxWait && (!(Rdy & XTE_RDY_HARD_ACS_RDY_MASK))) { + Rdy = XLlTemac_ReadReg(BaseAddress, XTE_RDY_OFFSET); + xdbg_stmnt( + if (MaxWait == 100) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "RDY reg not initially ready\n"); + } + ); + MaxWait--; + xdbg_stmnt( + if (MaxWait == 0) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "RDY reg never showed ready\n"); + } + ) + + } + Maw0Reg = XLlTemac_ReadReg(BaseAddress, XTE_LSW_OFFSET); + Maw1Reg = XLlTemac_ReadReg(BaseAddress, XTE_MSW_OFFSET); + + /* Copy the address to the user buffer */ + Aptr[0] = (u8) Maw0Reg; + Aptr[1] = (u8) (Maw0Reg >> 8); + Aptr[2] = (u8) (Maw0Reg >> 16); + Aptr[3] = (u8) (Maw0Reg >> 24); + Aptr[4] = (u8) Maw1Reg; + Aptr[5] = (u8) (Maw1Reg >> 8); + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_MulticastGet: done\n"); +} + +/*****************************************************************************/ +/** + * XLlTemac_MulticastClear clears the Ethernet address stored at index Entry + * in the TEMAC channel's multicast filter list.

+ * + * The device must be stopped to use this function.

+ * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param Entry is the HW storage location used when this address was added. + * It must be between 0..XTE_MULTI_MAT_ENTRIES-1. + * @param Entry is the hardware storage location to clear and must be between + * 0..XTE_MULTI_MAT_ENTRIES-1. + * + * @return On successful completion, XLlTemac_MulticastClear returns XST_SUCCESS. + * Otherwise, if the TEMAC channel is not stopped, XLlTemac_MulticastClear + * returns XST_DEVICE_IS_STARTED. + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +int XLlTemac_MulticastClear(XLlTemac *InstancePtr, int Entry) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + XASSERT_NONVOID(Entry < XTE_MULTI_MAT_ENTRIES); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_NONVOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_MulticastClear\n"); + + /* The device must be stopped before clearing the multicast hash table */ + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_MulticastClear: returning DEVICE_IS_STARTED\n"); + return (XST_DEVICE_IS_STARTED); + } + + /* Clear the entry by writing 0:0:0:0:0:0 to it */ + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_MAW0_OFFSET, 0); + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_MAW1_OFFSET, Entry << XTE_MAW1_MATADDR_SHIFT_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_MulticastClear: returning SUCCESS\n"); + return (XST_SUCCESS); +} + + +/*****************************************************************************/ +/** + * XLlTemac_SetMacPauseAddress sets the MAC address used for pause frames to + * AddressPtr. AddressPtr will be the address the TEMAC channel + * will recognize as being for pause frames. Pause frames transmitted with + * XLlTemac_SendPausePacket() will also use this address. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param AddressPtr is a pointer to the 6-byte Ethernet address to set. + * + * @return On successful completion, XLlTemac_SetMacPauseAddress returns + * XST_SUCCESS. Otherwise, if the TEMAC channel is not stopped, + * XLlTemac_SetMacPauseAddress returns XST_DEVICE_IS_STARTED. + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +int XLlTemac_SetMacPauseAddress(XLlTemac *InstancePtr, void *AddressPtr) +{ + u32 MacAddr; + u8 *Aptr = (u8 *) AddressPtr; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_NONVOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_SetMacPauseAddress\n"); + /* Be sure device has been stopped */ + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_SetMacPauseAddress: returning DEVICE_IS_STARTED\n"); + return (XST_DEVICE_IS_STARTED); + } + + /* Set the MAC bits [31:0] in ERXC0 */ + MacAddr = Aptr[0]; + MacAddr |= Aptr[1] << 8; + MacAddr |= Aptr[2] << 16; + MacAddr |= Aptr[3] << 24; + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW0_OFFSET, MacAddr); + + /* ERCW1 contains other info that must be preserved */ + MacAddr = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET); + MacAddr &= ~XTE_RCW1_PAUSEADDR_MASK; + + /* Set MAC bits [47:32] */ + MacAddr |= Aptr[4]; + MacAddr |= Aptr[5] << 8; + XLlTemac_WriteIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET, MacAddr); + + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_SetMacPauseAddress: returning SUCCESS\n"); + + return (XST_SUCCESS); +} + + +/*****************************************************************************/ +/** + * XLlTemac_GetMacPauseAddress gets the MAC address used for pause frames for the + * TEMAC channel specified by InstancePtr. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param AddressPtr references the memory buffer to store the retrieved MAC + * address. This memory buffer must be at least 6 bytes in length. + * + * @return N/A + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +void XLlTemac_GetMacPauseAddress(XLlTemac *InstancePtr, void *AddressPtr) +{ + u32 MacAddr; + u8 *Aptr = (u8 *) AddressPtr; + + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_VOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_SetMacPauseAddress\n"); + + /* Read MAC bits [31:0] in ERXC0 */ + MacAddr = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW0_OFFSET); + Aptr[0] = (u8) MacAddr; + Aptr[1] = (u8) (MacAddr >> 8); + Aptr[2] = (u8) (MacAddr >> 16); + Aptr[3] = (u8) (MacAddr >> 24); + + /* Read MAC bits [47:32] in RCW1 */ + MacAddr = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_RCW1_OFFSET); + Aptr[4] = (u8) MacAddr; + Aptr[5] = (u8) (MacAddr >> 8); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_SetMacPauseAddress: done\n"); +} + +/*****************************************************************************/ +/** + * XLlTemac_SendPausePacket sends a pause packet with the value of + * PauseValue. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param PauseValue is the pause value in units of 512 bit times. + * + * @return On successful completion, XLlTemac_SendPausePacket returns + * XST_SUCCESS. Otherwise, if the TEMAC channel is not started, + * XLlTemac_SendPausePacket returns XST_DEVICE_IS_STOPPED. + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +int XLlTemac_SendPausePacket(XLlTemac *InstancePtr, u16 PauseValue) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_NONVOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_SetMacPauseAddress\n"); + + /* Make sure device is ready for this operation */ + if (InstancePtr->IsStarted != XCOMPONENT_IS_STARTED) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_SendPausePacket: returning DEVICE_IS_STOPPED\n"); + return (XST_DEVICE_IS_STOPPED); + } + + /* Send flow control frame */ + XLlTemac_WriteReg(InstancePtr->Config.BaseAddress, XTE_TPF_OFFSET, + (u32) PauseValue & XTE_TPF_TPFV_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_SendPausePacket: returning SUCCESS\n"); + return (XST_SUCCESS); +} + +/*****************************************************************************/ +/** + * XLlTemac_GetSgmiiStatus get the state of the link when using the SGMII media + * interface. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param SpeedPtr references the location to store the result, which is the + * autonegotiated link speed in units of Mbits/sec, either 0, 10, 100, + * or 1000. + * + * @return On successful completion, XLlTemac_GetSgmiiStatus returns XST_SUCCESS. + * Otherwise, if TEMAC channel is not using an SGMII interface, + * XLlTemac_GetSgmiiStatus returns XST_NO_FEATURE. + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +int XLlTemac_GetSgmiiStatus(XLlTemac *InstancePtr, u16 *SpeedPtr) +{ + int PhyType; + u32 EgmicReg; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_NONVOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_GetSgmiiStatus\n"); + /* Make sure PHY is SGMII */ + PhyType = XLlTemac_GetPhysicalInterface(InstancePtr); + if (PhyType != XTE_PHY_TYPE_SGMII) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_GetSgmiiStatus: returning NO_FEATURE\n"); + return (XST_NO_FEATURE); + } + + /* Get the current contents of RGMII/SGMII config register */ + EgmicReg = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_PHYC_OFFSET); + + /* Extract speed */ + switch (EgmicReg & XTE_PHYC_SGMIILINKSPEED_MASK) { + case XTE_PHYC_SGLINKSPD_10: + *SpeedPtr = 10; + break; + + case XTE_PHYC_SGLINKSPD_100: + *SpeedPtr = 100; + break; + + case XTE_PHYC_SGLINKSPD_1000: + *SpeedPtr = 1000; + break; + + default: + *SpeedPtr = 0; + } + + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_GetSgmiiStatus: returning SUCCESS\n"); + return (XST_SUCCESS); +} + + +/*****************************************************************************/ +/** + * XLlTemac_GetRgmiiStatus get the state of the link when using the RGMII media + * interface. + * + * @param InstancePtr references the TEMAC channel on which to operate. + * @param SpeedPtr references the location to store the result, which is the + * autonegotiaged link speed in units of Mbits/sec, either 0, 10, 100, + * or 1000. + * @param IsFullDuplexPtr references the value to set to indicate full duplex + * operation. XLlTemac_GetRgmiiStatus sets IsFullDuplexPtr to TRUE + * when the RGMII link is operating in full duplex mode. Otherwise, + * XLlTemac_GetRgmiiStatus sets IsFullDuplexPtr to FALSE. + * @param IsLinkUpPtr references the value to set to indicate the link status. + * XLlTemac_GetRgmiiStatus sets IsLinkUpPtr to TRUE when the RGMII + * link up. Otherwise, XLlTemac_GetRgmiiStatus sets IsLinkUpPtr to + * FALSE. + * + * @return On successful completion, XLlTemac_GetRgmiiStatus returns XST_SUCCESS. + * Otherwise, if TEMAC channel is not using an RGMII interface, + * XLlTemac_GetRgmiiStatus returns XST_NO_FEATURE. + * + * @note + * + * This routine accesses the hard TEMAC registers through a shared interface + * between both channels of the TEMAC. Becuase of this, the application/OS code + * must provide mutual exclusive access to this routine with any of the other + * routines in this TEMAC driverr. + * + ******************************************************************************/ +int XLlTemac_GetRgmiiStatus(XLlTemac *InstancePtr, u16 *SpeedPtr, + int *IsFullDuplexPtr, int *IsLinkUpPtr) +{ + int PhyType; + u32 EgmicReg; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + /* + * If the mutual exclusion is enforced properly in the calling code, we + * should never get into the following case. + */ + XASSERT_NONVOID(XLlTemac_ReadReg(InstancePtr->Config.BaseAddress, + XTE_RDY_OFFSET) & XTE_RDY_HARD_ACS_RDY_MASK); + + xdbg_printf(XDBG_DEBUG_GENERAL, "XLlTemac_GetRgmiiStatus\n"); + /* Make sure PHY is RGMII */ + PhyType = XLlTemac_GetPhysicalInterface(InstancePtr); + if ((PhyType != XTE_PHY_TYPE_RGMII_1_3) && + (PhyType != XTE_PHY_TYPE_RGMII_2_0)) { + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_GetRgmiiStatus: returning NO_FEATURE\n"); + return (XST_NO_FEATURE); + } + + /* Get the current contents of RGMII/SGMII config register */ + EgmicReg = XLlTemac_ReadIndirectReg(InstancePtr->Config.BaseAddress, + XTE_PHYC_OFFSET); + + /* Extract speed */ + switch (EgmicReg & XTE_PHYC_RGMIILINKSPEED_MASK) { + case XTE_PHYC_RGLINKSPD_10: + *SpeedPtr = 10; + break; + + case XTE_PHYC_RGLINKSPD_100: + *SpeedPtr = 100; + break; + + case XTE_PHYC_RGLINKSPD_1000: + *SpeedPtr = 1000; + break; + + default: + *SpeedPtr = 0; + } + + /* Extract duplex and link status */ + if (EgmicReg & XTE_PHYC_RGMIIHD_MASK) { + *IsFullDuplexPtr = FALSE; + } + else { + *IsFullDuplexPtr = TRUE; + } + + if (EgmicReg & XTE_PHYC_RGMIILINK_MASK) { + *IsLinkUpPtr = TRUE; + } + else { + *IsLinkUpPtr = FALSE; + } + + xdbg_printf(XDBG_DEBUG_GENERAL, + "XLlTemac_GetRgmiiStatus: returning SUCCESS\n"); + return (XST_SUCCESS); +} + diff --git a/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac_hw.h b/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac_hw.h new file mode 100644 index 0000000..f468569 --- /dev/null +++ b/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac_hw.h @@ -0,0 +1,562 @@ +/* iId: */ +/****************************************************************************** +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" +* AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND +* SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, +* OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, +* APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION +* THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, +* AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE +* FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY +* WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE +* IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR +* REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF +* INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE. +* +* (c) Copyright 2005-2008 Xilinx Inc. +* All rights reserved. +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +* +******************************************************************************/ + +/*****************************************************************************/ +/** + * + * @file xlltemac_hw.h + * + * This header file contains identifiers and low-level driver functions (or + * macros) that can be used to access the Tri-Mode MAC Ethernet (TEMAC) device. + * High-level driver functions are defined in xlltemac.h. + * + * @note + * + * Some registers are not accessible when a HW instance is configured for SGDMA. + * + *
+ * MODIFICATION HISTORY:
+ *
+ * Ver   Who  Date     Changes
+ * ----- ---- -------- -------------------------------------------------------
+ * 1.00a jvb  11/10/06 First release
+ * 
+ * + ******************************************************************************/ + +#ifndef XTEMAC_HW_H /* prevent circular inclusions */ +#define XTEMAC_HW_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xio.h" +#include "xdebug.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/************************** Constant Definitions *****************************/ + +#define XTE_RESET_HARD_DELAY_US 4 /**< Us to delay for hard core reset */ + +/* Register offset definitions. Unless otherwise noted, register access is + * 32 bit. + */ + +/** @name Direct registers + * @{ + */ +#define XTE_RAF_OFFSET 0x00000000 /**< Reset and address filter */ +#define XTE_TPF_OFFSET 0x00000004 /**< Transmit pause frame */ +#define XTE_IFGP_OFFSET 0x00000008 /**< Transmit inter-frame gap adjustment */ +#define XTE_IS_OFFSET 0x0000000C /**< Interrupt status */ +#define XTE_IP_OFFSET 0x00000010 /**< Interrupt pending */ +#define XTE_IE_OFFSET 0x00000014 /**< Interrupt enable */ + +#define XTE_MSW_OFFSET 0x00000020 /**< Most significant word data */ +#define XTE_LSW_OFFSET 0x00000024 /**< Least significant word data */ +#define XTE_CTL_OFFSET 0x00000028 /**< Control */ +#define XTE_RDY_OFFSET 0x0000002C /**< Ready status */ +/*@}*/ + + +/** @name HARD_TEMAC Core Registers + * These are registers defined within the device's hard core located in the + * processor block. They are accessed indirectly through the registers, MSW, + * LSW, and CTL. + * + * Access to these registers should go through macros XLlTemac_ReadIndirectReg() + * and XLlTemac_WriteIndirectReg() to guarantee proper access. + * @{ + */ +#define XTE_RCW0_OFFSET 0x00000200 /**< Rx configuration word 0 */ +#define XTE_RCW1_OFFSET 0x00000240 /**< Rx configuration word 1 */ +#define XTE_TC_OFFSET 0x00000280 /**< Tx configuration */ +#define XTE_FCC_OFFSET 0x000002C0 /**< Flow control configuration */ +#define XTE_EMMC_OFFSET 0x00000300 /**< EMAC mode configuration */ +#define XTE_PHYC_OFFSET 0x00000320 /**< RGMII/SGMII configuration */ +#define XTE_MC_OFFSET 0x00000340 /**< Management configuration */ +#define XTE_UAW0_OFFSET 0x00000380 /**< Unicast address word 0 */ +#define XTE_UAW1_OFFSET 0x00000384 /**< Unicast address word 1 */ +#define XTE_MAW0_OFFSET 0x00000388 /**< Multicast address word 0 */ +#define XTE_MAW1_OFFSET 0x0000038C /**< Multicast address word 1 */ +#define XTE_AFM_OFFSET 0x00000390 /**< Address Filter (promiscuous) mode */ +#define XTE_TIS_OFFSET 0x000003A0 /**< Interrupt status */ +#define XTE_TIE_OFFSET 0x000003A4 /**< Interrupt enable */ +#define XTE_MIIMWD_OFFSET 0x000003B0 /**< MII management write data */ +#define XTE_MIIMAI_OFFSET 0x000003B4 /**< MII management access initiate */ +/*@}*/ + + +/* Register masks. The following constants define bit locations of various + * control bits in the registers. Constants are not defined for those registers + * that have a single bit field representing all 32 bits. For further + * information on the meaning of the various bit masks, refer to the HW spec. + */ + +/** @name Reset and Address Filter bits + * These bits are associated with the XTE_RAF_OFFSET register. + * @{ + */ +#define XTE_RAF_HTRST_MASK 0x00000001 /**< Hard TEMAC Reset */ +#define XTE_RAF_MCSTREJ_MASK 0x00000002 /**< Reject receive multicast destination address */ +#define XTE_RAF_BCSTREJ_MASK 0x00000004 /**< Reject receive broadcast destination address */ +/*@}*/ + +/** @name Transmit Pause Frame Register (TPF) + * @{ + */ +#define XTE_TPF_TPFV_MASK 0x0000FFFF /**< Tx pause frame value */ +/*@}*/ + +/** @name Transmit Inter-Frame Gap Adjustement Register (TFGP) + * @{ + */ +#define XTE_TFGP_IFGP_MASK 0x0000007F /**< Transmit inter-frame gap adjustment value */ +/*@}*/ + +/** @name Interrupt bits + * These bits are associated with the XTE_IS_OFFSET, XTE_IP_OFFSET, and + * XTE_IE_OFFSET registers. + * @{ + */ +#define XTE_INT_HARDACSCMPLT_MASK 0x00000001 /**< Hard register access complete */ +#define XTE_INT_AUTONEG_MASK 0x00000002 /**< Auto negotiation complete */ +#define XTE_INT_RC_MASK 0x00000004 /**< Receive complete */ +#define XTE_INT_RXRJECT_MASK 0x00000008 /**< Receive frame rejected */ +#define XTE_INT_RXFIFOOVR_MASK 0x00000010 /**< Receive fifo overrun */ +#define XTE_INT_TC_MASK 0x00000020 /**< Transmit complete */ +#define XTE_INT_ALL_MASK 0x0000003f /**< All the ints */ +/*@}*/ + + +#define XTE_INT_RECV_ERROR_MASK \ + (XTE_INT_RXRJECT_MASK | XTE_INT_RXFIFOOVR_MASK) /**< INT bits that indicate receive errors */ +/*@}*/ + + +/** @name Control Register (CTL) + * @{ + */ +#define XTE_CTL_WEN_MASK 0x00008000 /**< Write Enable */ +/*@}*/ + + +/** @name Ready Status, TEMAC Interrupt Status, TEMAC Interrupt Enable Registers + * (RDY, TIS, TIE) + * @{ + */ +#define XTE_RSE_FABR_RR_MASK 0x00000001 /**< Fabric read ready */ +#define XTE_RSE_MIIM_RR_MASK 0x00000002 /**< MII management read ready */ +#define XTE_RSE_MIIM_WR_MASK 0x00000004 /**< MII management write ready */ +#define XTE_RSE_AF_RR_MASK 0x00000008 /**< Address filter read ready*/ +#define XTE_RSE_AF_WR_MASK 0x00000010 /**< Address filter write ready*/ +#define XTE_RSE_CFG_RR_MASK 0x00000020 /**< Configuration register read ready*/ +#define XTE_RSE_CFG_WR_MASK 0x00000040 /**< Configuration register write ready*/ +#define XTE_RDY_HARD_ACS_RDY_MASK 0x00010000 /**< Hard register access ready */ +#define XTE_RDY_ALL (XTE_RSE_FABR_RR_MASK | \ + XTE_RSE_MIIM_RR_MASK | \ + XTE_RSE_MIIM_WR_MASK | \ + XTE_RSE_AF_RR_MASK | \ + XTE_RSE_AF_WR_MASK | \ + XTE_RSE_CFG_RR_MASK | \ + XTE_RSE_CFG_WR_MASK | \ + XTE_RDY_HARD_ACS_RDY_MASK) +/*@}*/ + + +/** @name Receive Configuration Word 1 (RCW1) + * @{ + */ +#define XTE_RCW1_RST_MASK 0x80000000 /**< Reset */ +#define XTE_RCW1_JUM_MASK 0x40000000 /**< Jumbo frame enable */ +#define XTE_RCW1_FCS_MASK 0x20000000 /**< In-Band FCS enable (FCS not stripped) */ +#define XTE_RCW1_RX_MASK 0x10000000 /**< Receiver enable */ +#define XTE_RCW1_VLAN_MASK 0x08000000 /**< VLAN frame enable */ +#define XTE_RCW1_HD_MASK 0x04000000 /**< Half duplex mode */ +#define XTE_RCW1_LT_DIS_MASK 0x02000000 /**< Length/type field valid check disable */ +#define XTE_RCW1_PAUSEADDR_MASK 0x0000FFFF /**< Pause frame source address + bits [47:32]. Bits [31:0] + are stored in register + RCW0 */ +/*@}*/ + + +/** @name Transmitter Configuration (TC) + * @{ + */ +#define XTE_TC_RST_MASK 0x80000000 /**< reset */ +#define XTE_TC_JUM_MASK 0x40000000 /**< Jumbo frame enable */ +#define XTE_TC_FCS_MASK 0x20000000 /**< In-Band FCS enable (FCS not generated) */ +#define XTE_TC_TX_MASK 0x10000000 /**< Transmitter enable */ +#define XTE_TC_VLAN_MASK 0x08000000 /**< VLAN frame enable */ +#define XTE_TC_HD_MASK 0x04000000 /**< Half duplex mode */ +#define XTE_TC_IFG_MASK 0x02000000 /**< Inter-frame gap adjustment enable */ +/*@}*/ + + +/** @name Flow Control Configuration (FCC) + * @{ + */ +#define XTE_FCC_FCRX_MASK 0x20000000 /**< Rx flow control enable */ +#define XTE_FCC_FCTX_MASK 0x40000000 /**< Tx flow control enable */ +/*@}*/ + + +/** @name EMAC Configuration (EMMC) + * @{ + */ +#define XTE_EMMC_LINKSPEED_MASK 0xC0000000 /**< Link speed */ +#define XTE_EMMC_RGMII_MASK 0x20000000 /**< RGMII mode enable */ +#define XTE_EMMC_SGMII_MASK 0x10000000 /**< SGMII mode enable */ +#define XTE_EMMC_GPCS_MASK 0x08000000 /**< 1000BaseX mode enable */ +#define XTE_EMMC_HOST_MASK 0x04000000 /**< Host interface enable */ +#define XTE_EMMC_TX16BIT 0x02000000 /**< 16 bit Tx client enable */ +#define XTE_EMMC_RX16BIT 0x01000000 /**< 16 bit Rx client enable */ + +#define XTE_EMMC_LINKSPD_10 0x00000000 /**< XTE_EMCFG_LINKSPD_MASK for + 10 Mbit */ +#define XTE_EMMC_LINKSPD_100 0x40000000 /**< XTE_EMCFG_LINKSPD_MASK for + 100 Mbit */ +#define XTE_EMMC_LINKSPD_1000 0x80000000 /**< XTE_EMCFG_LINKSPD_MASK for + 1000 Mbit */ +/*@}*/ + + +/** @name EMAC RGMII/SGMII Configuration (PHYC) + * @{ + */ +#define XTE_PHYC_SGMIILINKSPEED_MASK 0xC0000000 /**< SGMII link speed */ +#define XTE_PHYC_RGMIILINKSPEED_MASK 0x0000000C /**< RGMII link speed */ +#define XTE_PHYC_RGMIIHD_MASK 0x00000002 /**< RGMII Half-duplex mode */ +#define XTE_PHYC_RGMIILINK_MASK 0x00000001 /**< RGMII link status */ + +#define XTE_PHYC_RGLINKSPD_10 0x00000000 /**< XTE_GMIC_RGLINKSPD_MASK + for 10 Mbit */ +#define XTE_PHYC_RGLINKSPD_100 0x00000004 /**< XTE_GMIC_RGLINKSPD_MASK + for 100 Mbit */ +#define XTE_PHYC_RGLINKSPD_1000 0x00000008 /**< XTE_GMIC_RGLINKSPD_MASK + for 1000 Mbit */ +#define XTE_PHYC_SGLINKSPD_10 0x00000000 /**< XTE_SGMIC_RGLINKSPD_MASK + for 10 Mbit */ +#define XTE_PHYC_SGLINKSPD_100 0x40000000 /**< XTE_SGMIC_RGLINKSPD_MASK + for 100 Mbit */ +#define XTE_PHYC_SGLINKSPD_1000 0x80000000 /**< XTE_SGMIC_RGLINKSPD_MASK + for 1000 Mbit */ +/*@}*/ + + +/** @name EMAC Management Configuration (MC) + * @{ + */ +#define XTE_MC_MDIOEN_MASK 0x00000040 /**< MII management enable */ +#define XTE_MC_CLOCK_DIVIDE_MAX 0x3F /**< Maximum MDIO divisor */ +/*@}*/ + + +/** @name EMAC Unicast Address Register Word 1 (UAW1) + * @{ + */ +#define XTE_UAW1_UNICASTADDR_MASK 0x0000FFFF /**< Station address bits [47:32] + Station address bits [31:0] + are stored in register + UAW0 */ +/*@}*/ + + +/** @name EMAC Multicast Address Register Word 1 (MAW1) + * @{ + */ +#define XTE_MAW1_RNW_MASK 0x00800000 /**< Multicast address table register read enable */ +#define XTE_MAW1_ADDR_MASK 0x00030000 /**< Multicast address table register address */ +#define XTE_MAW1_MULTICADDR_MASK 0x0000FFFF /**< Multicast address bits [47:32] + Multicast address bits [31:0] + are stored in register + MAW0 */ +#define XTE_MAW1_MATADDR_SHIFT_MASK 16 /**< Number of bits to shift right + to align with + XTE_MAW1_CAMADDR_MASK */ +/*@}*/ + + +/** @name EMAC Address Filter Mode (AFM) + * @{ + */ +#define XTE_AFM_PM_MASK 0x80000000 /**< Promiscuous mode enable */ +/*@}*/ + + +/** @name Media Independent Interface Management (MIIM) + * @{ + */ +#define XTE_MIIM_REGAD_MASK 0x1F /**< MII Phy register address (REGAD) */ +#define XTE_MIIM_PHYAD_MASK 0x03E0 /**< MII Phy address (PHYAD) */ +#define XTE_MIIM_PHYAD_SHIFT 5 /**< MII Shift bits for PHYAD */ +/*@}*/ + + +/** @name Checksum offload buffer descriptor extensions + * @{ + */ +/** Byte offset where checksum should begin (16 bit word) */ +#define XTE_BD_TX_CSBEGIN_OFFSET XDMAV3_BD_USR0_OFFSET + +/** Offset where checksum should be inserted (16 bit word) */ +#define XTE_BD_TX_CSINSERT_OFFSET (XDMAV3_BD_USR0_OFFSET + 2) + +/** Checksum offload control for transmit (16 bit word) */ +#define XTE_BD_TX_CSCNTRL_OFFSET XDMAV3_BD_USR1_OFFSET + +/** Seed value for checksum calculation (16 bit word) */ +#define XTE_BD_TX_CSINIT_OFFSET (XDMAV3_BD_USR1_OFFSET + 2) + +/** Receive frame checksum calculation (16 bit word) */ +#define XTE_BD_RX_CSRAW_OFFSET (XDMAV3_BD_USR5_OFFSET + 2) + +/*@}*/ + +/** @name TX_CSCNTRL bit mask + * @{ + */ +#define XTE_BD_TX_CSCNTRL_CALC_MASK 0x0001 /**< Enable/disable Tx + checksum */ +/*@}*/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +xdbg_stmnt(extern int indent_on); + +#define XLlTemac_indent(RegOffset) \ + ((indent_on && ((RegOffset) >= XTE_RAF_OFFSET) && ((RegOffset) <= XTE_RDY_OFFSET)) ? "\t" : "") + +#define XLlTemac_reg_name(RegOffset) \ + (((RegOffset) == XTE_RAF_OFFSET) ? "XTE_RAF_OFFSET": \ + ((RegOffset) == XTE_TPF_OFFSET) ? "XTE_TPF_OFFSET": \ + ((RegOffset) == XTE_IFGP_OFFSET) ? "XTE_IFGP_OFFSET": \ + ((RegOffset) == XTE_IS_OFFSET) ? "XTE_IS_OFFSET": \ + ((RegOffset) == XTE_IP_OFFSET) ? "XTE_IP_OFFSET": \ + ((RegOffset) == XTE_IE_OFFSET) ? "XTE_IE_OFFSET": \ + ((RegOffset) == XTE_MSW_OFFSET) ? "XTE_MSW_OFFSET": \ + ((RegOffset) == XTE_LSW_OFFSET) ? "XTE_LSW_OFFSET": \ + ((RegOffset) == XTE_CTL_OFFSET) ? "XTE_CTL_OFFSET": \ + ((RegOffset) == XTE_RDY_OFFSET) ? "XTE_RDY_OFFSET": \ + ((RegOffset) == XTE_RCW0_OFFSET) ? "XTE_RCW0_OFFSET": \ + ((RegOffset) == XTE_RCW1_OFFSET) ? "XTE_RCW1_OFFSET": \ + ((RegOffset) == XTE_TC_OFFSET) ? "XTE_TC_OFFSET": \ + ((RegOffset) == XTE_FCC_OFFSET) ? "XTE_FCC_OFFSET": \ + ((RegOffset) == XTE_EMMC_OFFSET) ? "XTE_EMMC_OFFSET": \ + ((RegOffset) == XTE_PHYC_OFFSET) ? "XTE_PHYC_OFFSET": \ + ((RegOffset) == XTE_MC_OFFSET) ? "XTE_MC_OFFSET": \ + ((RegOffset) == XTE_UAW0_OFFSET) ? "XTE_UAW0_OFFSET": \ + ((RegOffset) == XTE_UAW1_OFFSET) ? "XTE_UAW1_OFFSET": \ + ((RegOffset) == XTE_MAW0_OFFSET) ? "XTE_MAW0_OFFSET": \ + ((RegOffset) == XTE_MAW1_OFFSET) ? "XTE_MAW1_OFFSET": \ + ((RegOffset) == XTE_AFM_OFFSET) ? "XTE_AFM_OFFSET": \ + ((RegOffset) == XTE_TIS_OFFSET) ? "XTE_TIS_OFFSET": \ + ((RegOffset) == XTE_TIE_OFFSET) ? "XTE_TIE_OFFSET": \ + ((RegOffset) == XTE_MIIMWD_OFFSET) ? "XTE_MIIMWD_OFFSET": \ + ((RegOffset) == XTE_MIIMAI_OFFSET) ? "XTE_MIIMAI_OFFSET": \ + "unknown") + +#define XLlTemac_print_reg_o(BaseAddress, RegOffset, Value) \ + xdbg_printf(XDBG_DEBUG_TEMAC_REG, "%s0x%0x -> %s(0x%0x)\n", \ + XLlTemac_indent(RegOffset), (Value), \ + XLlTemac_reg_name(RegOffset), (RegOffset)) \ + +#define XLlTemac_print_reg_i(BaseAddress, RegOffset, Value) \ + xdbg_printf(XDBG_DEBUG_TEMAC_REG, "%s%s(0x%0x) -> 0x%0x\n", \ + XLlTemac_indent(RegOffset), XLlTemac_reg_name(RegOffset), \ + (RegOffset), (Value)) \ + +/****************************************************************************/ +/** + * + * XLlTemac_ReadReg returns the value read from the register specified by + * RegOffset. + * + * @param BaseAddress is the base address of the TEMAC channel. + * @param RegOffset is the offset of the register to be read. + * + * @return XLlTemac_ReadReg returns the 32-bit value of the register. + * + * @note + * C-style signature: + * u32 XLlTemac_mReadReg(u32 BaseAddress, u32 RegOffset) + * + *****************************************************************************/ +#ifdef DEBUG +#define XLlTemac_ReadReg(BaseAddress, RegOffset) \ +({ \ + u32 value; \ + if ((RegOffset) > 0x2c) { \ + printf ("readreg: Woah! wrong reg addr: 0x%0x\n", (RegOffset)); \ + } \ + value = XIo_In32(((BaseAddress) + (RegOffset))); \ + XLlTemac_print_reg_i((BaseAddress), (RegOffset), value); \ + value; \ +}) +#else +#define XLlTemac_ReadReg(BaseAddress, RegOffset) \ + (XIo_In32(((BaseAddress) + (RegOffset)))) +#endif + +/****************************************************************************/ +/** + * + * XLlTemac_WriteReg, writes Data to the register specified by + * RegOffset. + * + * @param BaseAddress is the base address of the TEMAC channel. + * @param RegOffset is the offset of the register to be written. + * @param Data is the 32-bit value to write to the register. + * + * @return N/A + * + * @note + * C-style signature: + * void XLlTemac_mWriteReg(u32 BaseAddress, u32 RegOffset, u32 Data) + * + *****************************************************************************/ +#ifdef DEBUG +#define XLlTemac_WriteReg(BaseAddress, RegOffset, Data) \ +({ \ + if ((RegOffset) > 0x2c) { \ + printf ("writereg: Woah! wrong reg addr: 0x%0x\n", (RegOffset)); \ + } \ + XLlTemac_print_reg_o((BaseAddress), (RegOffset), (Data)); \ + XIo_Out32(((BaseAddress) + (RegOffset)), (Data)); \ +}) +#else +#define XLlTemac_WriteReg(BaseAddress, RegOffset, Data) \ + XIo_Out32(((BaseAddress) + (RegOffset)), (Data)) +#endif + +/****************************************************************************/ +/** + * + * XLlTemac_ReadIndirectReg returns the value read from the hard TEMAC register + * specified by RegOffset. + * + * @param BaseAddress is the base address of the TEMAC channel. + * @param RegOffset is the offset of the hard TEMAC register to be read. + * + * @return XLlTemac_ReadIndirectReg returns the 32-bit value of the register. + * + * @note + * C-style signature: + * u32 XLlTemac_mReadIndirectReg(u32 BaseAddress, u32 RegOffset) + * + *****************************************************************************/ +#ifdef DEBUG +extern u32 _xlltemac_rir_value; + +#define XLlTemac_ReadIndirectReg(BaseAddress, RegOffset) \ +( \ + indent_on = 1, \ + (((RegOffset) < 0x200) ? \ + xdbg_printf(XDBG_DEBUG_ERROR, \ + "readindirect: Woah! wrong reg addr: 0x%0x\n", \ + (RegOffset)) : 0), \ + (((RegOffset) > 0x3b4) ? \ + xdbg_printf(XDBG_DEBUG_ERROR, \ + "readindirect: Woah! wrong reg addr: 0x%0x\n", \ + (RegOffset)) : 0), \ + XLlTemac_WriteReg((BaseAddress), XTE_CTL_OFFSET, (RegOffset)), \ + _xlltemac_rir_value = XLlTemac_ReadReg((BaseAddress), XTE_LSW_OFFSET), \ + XLlTemac_print_reg_i((BaseAddress), (RegOffset), _xlltemac_rir_value), \ + indent_on = 0, \ + _xlltemac_rir_value \ +) +#else +#define XLlTemac_ReadIndirectReg(BaseAddress, RegOffset) \ +( \ + XLlTemac_WriteReg((BaseAddress), XTE_CTL_OFFSET, (RegOffset)), \ + XLlTemac_ReadReg((BaseAddress), XTE_LSW_OFFSET) \ +) +#endif + +/****************************************************************************/ +/** + * + * XLlTemac_WriteIndirectReg, writes Data to the hard TEMAC register + * specified by RegOffset. + * + * @param BaseAddress is the base address of the TEMAC channel. + * @param RegOffset is the offset of the hard TEMAC register to be written. + * @param Data is the 32-bit value to write to the register. + * + * @return N/A + * + * @note + * C-style signature: + * void XLlTemac_WriteIndirectReg(u32 BaseAddress, u32 RegOffset, u32 Data) + * + *****************************************************************************/ +#ifdef DEBUG +#define XLlTemac_WriteIndirectReg(BaseAddress, RegOffset, Data) \ +( \ + indent_on = 1, \ + (((RegOffset) < 0x200) ? \ + xdbg_printf(XDBG_DEBUG_ERROR, \ + "readindirect: Woah! wrong reg addr: 0x%0x\n", \ + (RegOffset)) : 0), \ + (((RegOffset) > 0x3b4) ? \ + xdbg_printf(XDBG_DEBUG_ERROR, \ + "readindirect: Woah! wrong reg addr: 0x%0x\n", \ + (RegOffset)) : 0), \ + XLlTemac_print_reg_o((BaseAddress), (RegOffset), (Data)), \ + XLlTemac_WriteReg((BaseAddress), XTE_LSW_OFFSET, (Data)), \ + XLlTemac_WriteReg((BaseAddress), XTE_CTL_OFFSET, \ + ((RegOffset) | XTE_CTL_WEN_MASK)), \ + ((XLlTemac_ReadReg((BaseAddress), XTE_RDY_OFFSET) & \ + XTE_RDY_HARD_ACS_RDY_MASK) ? \ + ((XLlTemac_ReadIndirectReg((BaseAddress), (RegOffset)) != (Data)) ? \ + xdbg_printf(XDBG_DEBUG_ERROR, \ + "data written is not read back: Reg: 0x%0x\n", \ + (RegOffset)) \ + : 0) \ + : xdbg_printf(XDBG_DEBUG_ERROR, "(temac_wi) RDY reg not initially ready\n")), \ + indent_on = 0 \ +) +#else +#define XLlTemac_WriteIndirectReg(BaseAddress, RegOffset, Data) \ + XLlTemac_WriteReg((BaseAddress), XTE_LSW_OFFSET, (Data)), \ + XLlTemac_WriteReg((BaseAddress), XTE_CTL_OFFSET, \ + ((RegOffset) | XTE_CTL_WEN_MASK)) +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* end of protection macro */ diff --git a/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac_main.c b/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac_main.c new file mode 100644 index 0000000..d02c10c --- /dev/null +++ b/drivers/net/ethernet/xilinx/xilinx_lltemac/xlltemac_main.c @@ -0,0 +1,3850 @@ +/* + * Xilinx Ethernet: Linux driver for the XPS_LLTEMAC core. + * + * Author: Xilinx, Inc. + * + * 2006-2007 (c) Xilinx, Inc. This file is licensed uner the terms of the GNU + * General Public License version 2.1. This program is licensed "as is" without + * any warranty of any kind, whether express or implied. + * + *
+ * MODIFICATION HISTORY:
+ *
+ * Ver   Who  Date     Changes
+ * ----- ---- -------- -------------------------------------------------------
+ * 1.00a jvb  05/08/05 First release
+ * 
+ * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +// For open firmware. +#include +#include +#include +#include +#endif + +#include "xbasic_types.h" +#include "xlltemac.h" +#include "xllfifo.h" +#include "xlldma.h" +#include "xlldma_bdring.h" + +#define LOCAL_FEATURE_RX_CSUM 0x01 + +/* + * Default SEND and RECV buffer descriptors (BD) numbers. + * BD Space needed is (XTE_SEND_BD_CNT+XTE_RECV_BD_CNT)*Sizeof(XLlDma_Bd). + * Each XLlDma_Bd instance currently takes 40 bytes. + */ +#define XTE_SEND_BD_CNT 256 +#define XTE_RECV_BD_CNT 256 + +/* Must be shorter than length of ethtool_drvinfo.driver field to fit */ +#define DRIVER_NAME "xilinx_lltemac" +#define DRIVER_DESCRIPTION "Xilinx Tri-Mode Ethernet MAC driver" +#define DRIVER_VERSION "1.00a" + +#define TX_TIMEOUT (3*HZ) /* Transmission timeout is 3 seconds. */ + +/* + * This version of the Xilinx TEMAC uses external DMA or FIFO cores. + * Currently neither the DMA or FIFO cores used require any memory alignment + * restrictions. + */ +/* + * ALIGNMENT_RECV = the alignement required to receive + * ALIGNMENT_SEND = the alignement required to send + * ALIGNMENT_SEND_PERF = tx alignment for better performance + * + * ALIGNMENT_SEND is used to see if we *need* to copy the data to re-align. + * ALIGNMENT_SEND_PERF is used if we've decided we need to copy anyway, we just + * copy to this alignment for better performance. + */ + +#define ALIGNMENT_RECV 34 +#define ALIGNMENT_SEND 8 +#define ALIGNMENT_SEND_PERF 32 + +#define XTE_SEND 1 +#define XTE_RECV 2 + +/* SGDMA buffer descriptors must be aligned on a 8-byte boundary. */ +#define ALIGNMENT_BD XLLDMA_BD_MINIMUM_ALIGNMENT + +/* BUFFER_ALIGN(adr) calculates the number of bytes to the next alignment. */ +#define BUFFER_ALIGNSEND(adr) ((ALIGNMENT_SEND - ((u32) adr)) % ALIGNMENT_SEND) +#define BUFFER_ALIGNSEND_PERF(adr) ((ALIGNMENT_SEND_PERF - ((u32) adr)) % 32) +#define BUFFER_ALIGNRECV(adr) ((ALIGNMENT_RECV - ((u32) adr)) % 32) + +/* Default TX/RX Threshold and waitbound values for SGDMA mode */ +#define DFT_TX_THRESHOLD 24 +#define DFT_TX_WAITBOUND 254 +#define DFT_RX_THRESHOLD 4 +#define DFT_RX_WAITBOUND 254 + +#define XTE_AUTOSTRIPPING 1 + +/* Put Buffer Descriptors in BRAM? + * NOTE: + * Putting BDs in BRAM only works if there is only ONE instance of the TEMAC + * in hardware. The code does not handle multiple instances, e.g. it does + * not manage the memory in BRAM. + */ +#define BD_IN_BRAM 0 +#define BRAM_BASEADDR 0xffff8000 + + +/* + * Checksum offload macros + */ +#define BdCsumEnable(BdPtr) \ + XLlDma_mBdWrite((BdPtr), XLLDMA_BD_STSCTRL_USR0_OFFSET, \ + (XLlDma_mBdRead((BdPtr), XLLDMA_BD_STSCTRL_USR0_OFFSET)) | 1 ) + +/* Used for debugging */ +#define BdCsumEnabled(BdPtr) \ + ((XLlDma_mBdRead((BdPtr), XLLDMA_BD_STSCTRL_USR0_OFFSET)) & 1) + +#define BdCsumDisable(BdPtr) \ + XLlDma_mBdWrite((BdPtr), XLLDMA_BD_STSCTRL_USR0_OFFSET, \ + (XLlDma_mBdRead((BdPtr), XLLDMA_BD_STSCTRL_USR0_OFFSET)) & 0xFFFFFFFE ) + +#define BdCsumSetup(BdPtr, Start, Insert) \ + XLlDma_mBdWrite((BdPtr), XLLDMA_BD_USR1_OFFSET, ((Start) << 16) | (Insert)) + +/* Used for debugging */ +#define BdCsumInsert(BdPtr) \ + (XLlDma_mBdRead((BdPtr), XLLDMA_BD_USR1_OFFSET) & 0xffff) + +#define BdCsumSeed(BdPtr, Seed) \ + XLlDma_mBdWrite((BdPtr), XLLDMA_BD_USR2_OFFSET, 0) + +#define BdCsumGet(BdPtr) \ + (XLlDma_mBdRead((BdPtr), XLLDMA_BD_USR3_OFFSET) & 0xffff) + +#define BdGetRxLen(BdPtr) \ + (XLlDma_mBdRead((BdPtr), XLLDMA_BD_USR4_OFFSET) & 0x3fff) + +/* LLTEMAC platform data */ +struct xlltemac_platform_data { + u8 tx_csum; + u8 rx_csum; + u8 phy_type; + u8 dcr_host; + u8 ll_dev_type; + u32 ll_dev_baseaddress; + u32 ll_dev_dma_rx_irq; + u32 ll_dev_dma_tx_irq; + u32 ll_dev_fifo_irq; + + u8 mac_addr[6]; +}; + +/* + * Our private per device data. When a net_device is allocated we will + * ask for enough extra space for this. + */ +struct net_local { + struct list_head rcv; + struct list_head xmit; + + struct net_device *ndev; /* this device */ + struct net_device *next_dev; /* The next device in dev_list */ + struct net_device_stats stats; /* Statistics for this device */ + struct timer_list phy_timer; /* PHY monitoring timer */ + + u32 index; /* Which interface is this */ +#if 0 + XInterruptHandler Isr; /* Pointer to the XLlTemac ISR routine */ +#endif + u8 gmii_addr; /* The GMII address of the PHY */ + u32 virt_dma_addr; /* Virtual address to mapped dma */ + + /* The underlying OS independent code needs space as well. A + * pointer to the following XLlTemac structure will be passed to + * any XLlTemac_ function that requires it. However, we treat the + * data as an opaque object in this file (meaning that we never + * reference any of the fields inside of the structure). */ + XLlFifo Fifo; + XLlDma Dma; + XLlTemac Emac; + + unsigned int fifo_irq; /* fifo irq */ + unsigned int dma_irq_s; /* send irq */ + unsigned int dma_irq_r; /* recv irq */ + unsigned int frame_size; /* actual frame size = mtu + padding */ + + int cur_speed; + + /* Buffer Descriptor space for both TX and RX BD ring */ + void *desc_space; /* virtual address of BD space */ + dma_addr_t desc_space_handle; /* physical address of BD space */ + int desc_space_size; /* size of BD space */ + + /* buffer for one skb in case no room is available for transmission */ + struct sk_buff *deferred_skb; + + /* send buffers for non tx-dre hw */ + void **tx_orig_buffers; /* Buffer addresses as returned by + dma_alloc_coherent() */ + void **tx_buffers; /* Buffers addresses aligned for DMA */ + dma_addr_t *tx_phys_buffers; /* Buffer addresses in physical memory */ + size_t tx_buffers_cur; /* Index of current buffer used */ + + /* stats */ + int max_frags_in_a_packet; + unsigned long realignments; + unsigned long tx_hw_csums; + unsigned long rx_hw_csums; + unsigned long local_features; +#if ! XTE_AUTOSTRIPPING + unsigned long stripping; +#endif +}; + +static u32 dma_rx_int_mask = XLLDMA_CR_IRQ_ALL_EN_MASK; +static u32 dma_tx_int_mask = XLLDMA_CR_IRQ_ALL_EN_MASK; + +/* for exclusion of all program flows (processes, ISRs and BHs) */ +static spinlock_t XTE_spinlock; /* = SPIN_LOCK_UNLOCKED; */ +static spinlock_t XTE_tx_spinlock; /* = SPIN_LOCK_UNLOCKED; */ +static spinlock_t XTE_rx_spinlock; /* = SPIN_LOCK_UNLOCKED; */ + +/* + * ethtool has a status reporting feature where we can report any sort of + * status information we'd like. This is the list of strings used for that + * status reporting. ETH_GSTRING_LEN is defined in ethtool.h + */ +static char xenet_ethtool_gstrings_stats[][ETH_GSTRING_LEN] = { + "txpkts", "txdropped", "txerr", "txfifoerr", + "rxpkts", "rxdropped", "rxerr", "rxfifoerr", + "rxrejerr", "max_frags", "tx_hw_csums", "rx_hw_csums", +}; + +#define XENET_STATS_LEN sizeof(xenet_ethtool_gstrings_stats) / ETH_GSTRING_LEN + +/* Helper function to determine if a given XLlTemac error warrants a reset. */ +extern inline int status_requires_reset(int s) +{ + return (s == XST_FIFO_ERROR || + s == XST_PFIFO_DEADLOCK || + s == XST_DMA_ERROR || s == XST_IPIF_ERROR); +} + +/* Queues with locks */ +static LIST_HEAD(receivedQueue); +static spinlock_t receivedQueueSpin; // = SPIN_LOCK_UNLOCKED; + +static LIST_HEAD(sentQueue); +static spinlock_t sentQueueSpin; // = SPIN_LOCK_UNLOCKED; + + +/* from mii.h + * + * Items in mii.h but not in gmii.h + */ +#define ADVERTISE_100FULL 0x0100 +#define ADVERTISE_100HALF 0x0080 +#define ADVERTISE_10FULL 0x0040 +#define ADVERTISE_10HALF 0x0020 +#define ADVERTISE_CSMA 0x0001 + +#define EX_ADVERTISE_1000FULL 0x0200 +#define EX_ADVERTISE_1000HALF 0x0100 + +/* + * items not in mii.h nor gmii.h but should be + */ +#define MII_EXADVERTISE 0x09 + +/* + * Wrap certain temac routines with a lock, so access to the shared hard temac + * interface is accessed mutually exclusive for dual channel temac support. + */ + +static inline void _XLlTemac_Start(XLlTemac *InstancePtr) +{ + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + XLlTemac_Start(InstancePtr); + spin_unlock_irqrestore(&XTE_spinlock, flags); +} + +static inline void _XLlTemac_Stop(XLlTemac *InstancePtr) +{ + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + XLlTemac_Stop(InstancePtr); + spin_unlock_irqrestore(&XTE_spinlock, flags); +} + +static inline void _XLlTemac_Reset(XLlTemac *InstancePtr, int HardCoreAction) +{ + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + XLlTemac_Reset(InstancePtr, HardCoreAction); + spin_unlock_irqrestore(&XTE_spinlock, flags); +} + +static inline int _XLlTemac_SetMacAddress(XLlTemac *InstancePtr, + void *AddressPtr) +{ + int status; + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + status = XLlTemac_SetMacAddress(InstancePtr, AddressPtr); + spin_unlock_irqrestore(&XTE_spinlock, flags); + + return status; +} + +static inline void _XLlTemac_GetMacAddress(XLlTemac *InstancePtr, + void *AddressPtr) +{ + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + XLlTemac_GetMacAddress(InstancePtr, AddressPtr); + spin_unlock_irqrestore(&XTE_spinlock, flags); +} + +static inline int _XLlTemac_SetOptions(XLlTemac *InstancePtr, u32 Options) +{ + int status; + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + status = XLlTemac_SetOptions(InstancePtr, Options); + spin_unlock_irqrestore(&XTE_spinlock, flags); + + return status; +} + +static inline int _XLlTemac_ClearOptions(XLlTemac *InstancePtr, u32 Options) +{ + int status; + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + status = XLlTemac_ClearOptions(InstancePtr, Options); + spin_unlock_irqrestore(&XTE_spinlock, flags); + + return status; +} + +static inline u16 _XLlTemac_GetOperatingSpeed(XLlTemac *InstancePtr) +{ + u16 speed; + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + speed = XLlTemac_GetOperatingSpeed(InstancePtr); + spin_unlock_irqrestore(&XTE_spinlock, flags); + + return speed; +} + +static inline void _XLlTemac_SetOperatingSpeed(XLlTemac *InstancePtr, u16 Speed) +{ + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + XLlTemac_SetOperatingSpeed(InstancePtr, Speed); + spin_unlock_irqrestore(&XTE_spinlock, flags); + + /* We need a delay after we set the speed. Otherwise the PHY will not be ready. */ + udelay(10000); +} + +static inline void _XLlTemac_PhySetMdioDivisor(XLlTemac *InstancePtr, u8 Divisor) +{ + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + XLlTemac_PhySetMdioDivisor(InstancePtr, Divisor); + spin_unlock_irqrestore(&XTE_spinlock, flags); +} + +static inline void _XLlTemac_PhyRead(XLlTemac *InstancePtr, u32 PhyAddress, + u32 RegisterNum, u16 *PhyDataPtr) +{ + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + XLlTemac_PhyRead(InstancePtr, PhyAddress, RegisterNum, PhyDataPtr); + spin_unlock_irqrestore(&XTE_spinlock, flags); +} + +static inline void _XLlTemac_PhyWrite(XLlTemac *InstancePtr, u32 PhyAddress, + u32 RegisterNum, u16 PhyData) +{ + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + XLlTemac_PhyWrite(InstancePtr, PhyAddress, RegisterNum, PhyData); + spin_unlock_irqrestore(&XTE_spinlock, flags); +} + + +static inline int _XLlTemac_MulticastClear(XLlTemac *InstancePtr, int Entry) +{ + int status; + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + status = XLlTemac_MulticastClear(InstancePtr, Entry); + spin_unlock_irqrestore(&XTE_spinlock, flags); + + return status; +} + +static inline int _XLlTemac_SetMacPauseAddress(XLlTemac *InstancePtr, void *AddressPtr) +{ + int status; + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + status = XLlTemac_SetMacPauseAddress(InstancePtr, AddressPtr); + spin_unlock_irqrestore(&XTE_spinlock, flags); + + return status; +} + +static inline void _XLlTemac_GetMacPauseAddress(XLlTemac *InstancePtr, void *AddressPtr) +{ + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + XLlTemac_GetMacPauseAddress(InstancePtr, AddressPtr); + spin_unlock_irqrestore(&XTE_spinlock, flags); +} + +static inline int _XLlTemac_GetSgmiiStatus(XLlTemac *InstancePtr, u16 *SpeedPtr) +{ + int status; + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + status = XLlTemac_GetSgmiiStatus(InstancePtr, SpeedPtr); + spin_unlock_irqrestore(&XTE_spinlock, flags); + + return status; +} + +static inline int _XLlTemac_GetRgmiiStatus(XLlTemac *InstancePtr, + u16 *SpeedPtr, + int *IsFullDuplexPtr, + int *IsLinkUpPtr) +{ + int status; + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + status = XLlTemac_GetRgmiiStatus(InstancePtr, SpeedPtr, IsFullDuplexPtr, IsLinkUpPtr); + spin_unlock_irqrestore(&XTE_spinlock, flags); + + return status; +} + + +#ifdef CONFIG_XILINX_LLTEMAC_MARVELL_88E1111_RGMII +#define MARVELL_88E1111_EXTENDED_PHY_CTL_REG_OFFSET 20 +#define MARVELL_88E1111_EXTENDED_PHY_STATUS_REG_OFFSET 27 +#endif + +#ifdef CONFIG_XILINX_LLTEMAC_NATIONAL_DP83865_GMII +# define NATIONAL_DP83865_CONTROL_INIT 0x9200 +# define NATIONAL_DP83865_CONTROL 0 +# define NATIONAL_DP83865_STATUS 1 +# define NATIONAL_DP83865_STATUS_LINK 0x04 +# define NATIONAL_DP83865_STATUS_AUTONEGEND 0x20 +# define NATIONAL_DP83865_STATUS_AUTONEG 0x11 +# define NATIONAL_DP83865_LINKSPEED_1000M 0x10 +# define NATIONAL_DP83865_LINKSPEED_100M 0x8 +# define NATIONAL_DP83865_LINKSPEED_MASK 0x18 +# define NATIONAL_DP83865_RETRIES 5 +#endif + +#define DEBUG_ERROR KERN_ERR +#define DEBUG_LOG(level, ...) printk(level __VA_ARGS__) + +/* + * Perform any necessary special phy setup. In the gmii case, nothing needs to + * be done. + */ +static void phy_setup(struct net_local *lp) +{ +#ifdef CONFIG_XILINX_LLTEMAC_NATIONAL_DP83865_GMII + u16 RegValue; + + printk(KERN_INFO "NATIONAL DP83865 PHY\n"); + RegValue = NATIONAL_DP83865_CONTROL_INIT; + /*Do not reset phy*/ + _XLlTemac_PhyWrite(&lp->Emac, lp->gmii_addr, + NATIONAL_DP83865_CONTROL, RegValue); + + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, + NATIONAL_DP83865_STATUS, &RegValue); + + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, + NATIONAL_DP83865_STATUS, &RegValue); +#endif +#ifdef CONFIG_XILINX_LLTEMAC_MARVELL_88E1111_RGMII + u16 Register; + + /* + * Set up MAC interface + * + * Write 0x0cc3 to reg 20 in PHY + * 5432 1098 7654 3210 + * ---- ---- ---- ---- + * 0cc3=0000 1100 1100 0011 + * downshift counter (bits 11-9): 110 = 7 times + * downshift enable (bit 8): 0 = enable + * RGMII timing control (bit 7): 1 = add delay to rx clk ro rxd + * outputs + * Default Mac interface speed (bits 6-4): 100 = 10mbps 2.5 mhz + * (between phy and temac - gets renegotiated) + * reserved (bit 3) + * DTE detect (bit 2): 0 disabled + * RGMII transmit timing control (bit 1): 1 = add delay + * to tx clk ro txd outputs + * Transmitter Disable (bit 0): 1 = enabled + */ + _XLlTemac_PhyWrite(&lp->Emac, lp->gmii_addr, MARVELL_88E1111_EXTENDED_PHY_CTL_REG_OFFSET, 0x0cc3); + + /* + * Set RGMII to copper with correct hysterisis and correct mode + * Disable fiber/copper auto sel, choose copper + * RGMII /Modified MII to copper mode + * + * Write 0x848b to reg 27 + * 5432 1098 7654 3210 + * ---- ---- ---- ---- + * 848b=1000 0100 1000 1011 + * Fiber/Copper Auto Selection (bit 15): 1 = disable auto selection + * Interrupt Polarity (bit 10): 1 = int active low + * DTE detect status drop hysteresis (bts 8-5): 0100 = report 20s after DTE power status drop + * HWCFG mode (bits 3-0): 1011 = RGMII/Modified MII to Copper + */ + _XLlTemac_PhyWrite(&lp->Emac, lp->gmii_addr, MARVELL_88E1111_EXTENDED_PHY_STATUS_REG_OFFSET, 0x848b); + + /* + * Reset the PHY + */ + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_BMCR, &Register); + Register |= BMCR_RESET; + _XLlTemac_PhyWrite(&lp->Emac, lp->gmii_addr, MII_BMCR, Register); + +#endif /* CONFIG_XILINX_LLTEMAC_MARVELL_88E1111_RGMII */ + +#ifdef CONFIG_XILINX_LLTEMAC_XILINX_1000BASEX + u16 Register; + + /* + * Setup the PHY control register + */ + Register = BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_ANENABLE; + _XLlTemac_PhyWrite(&lp->Emac, lp->gmii_addr, MII_BMCR, Register); + +#endif /* CONFIG_XILINX_LLTEMAC_XILINX_1000BASEX */ +} + + +typedef enum DUPLEX { UNKNOWN_DUPLEX, HALF_DUPLEX, FULL_DUPLEX } DUPLEX; + +static int renegotiate_speed(struct net_device *dev, int speed, DUPLEX duplex) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + int retries = 2; + int wait_count; + u16 phy_reg0 = BMCR_ANENABLE | BMCR_ANRESTART; + u16 phy_reg1; + u16 phy_reg4; + u16 phy_reg9 = 0; + + + /* + * It appears that the 10baset full and half duplex settings + * are overloaded for gigabit ethernet + */ + if ((duplex == FULL_DUPLEX) && (speed == 10)) { + phy_reg4 = ADVERTISE_10FULL | ADVERTISE_CSMA; + } + else if ((duplex == FULL_DUPLEX) && (speed == 100)) { + phy_reg4 = ADVERTISE_100FULL | ADVERTISE_CSMA; + } + else if ((duplex == FULL_DUPLEX) && (speed == 1000)) { + phy_reg4 = ADVERTISE_CSMA; + phy_reg9 = EX_ADVERTISE_1000FULL; + } + else if (speed == 10) { + phy_reg4 = ADVERTISE_10HALF | ADVERTISE_CSMA; + } + else if (speed == 100) { + phy_reg4 = ADVERTISE_100HALF | ADVERTISE_CSMA; + } + else if (speed == 1000) { + phy_reg4 = ADVERTISE_CSMA; + phy_reg9 = EX_ADVERTISE_1000HALF; + } + else { + printk(KERN_ERR + "%s: XLlTemac: unsupported speed requested: %d\n", + dev->name, speed); + return -1; + } + + /* + * link status in register 1: + * first read / second read: + * 0 0 link is down + * 0 1 link is up (but it was down earlier) + * 1 0 link is down (but it was just up) + * 1 1 link is up + * + */ + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_BMSR, &phy_reg1); + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_BMSR, &phy_reg1); + _XLlTemac_PhyWrite(&lp->Emac, lp->gmii_addr, MII_ADVERTISE, phy_reg4); + _XLlTemac_PhyWrite(&lp->Emac, lp->gmii_addr, MII_EXADVERTISE, phy_reg9); + +#ifndef CONFIG_XILINX_LLTEMAC_NATIONAL_DP83865_GMII + while (retries--) { + /* initiate an autonegotiation of the speed */ + _XLlTemac_PhyWrite(&lp->Emac, lp->gmii_addr, MII_BMCR, phy_reg0); + + wait_count = 10; /* so we don't loop forever */ + while (wait_count--) { + /* wait a bit for the negotiation to complete */ + mdelay(500); + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_BMSR, + &phy_reg1); + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_BMSR, + &phy_reg1); + + if ((phy_reg1 & BMSR_LSTATUS) && + (phy_reg1 & BMSR_ANEGCOMPLETE)) + break; + + } + + if (phy_reg1 & BMSR_LSTATUS) { + printk(KERN_INFO + "%s: XLlTemac: We renegotiated the speed to: %d\n", + dev->name, speed); + return 0; + } + else { + printk(KERN_ERR + "%s: XLlTemac: Not able to set the speed to %d (status: 0x%0x)\n", + dev->name, speed, phy_reg1); + return -1; + } + } + + printk(KERN_ERR + "%s: XLlTemac: Not able to set the speed to %d\n", dev->name, + speed); + return -1; +#else + printk(KERN_INFO + "%s: XLlTemac: We renegotiated the speed to: %d\n", + dev->name, speed); + return 0; +#endif +} + +/* + * This function sets up MAC's speed according to link speed of PHY + */ +static void set_mac_speed(struct net_local *lp) +{ + u16 phylinkspeed; + struct net_device *dev = lp->ndev; + +#ifdef CONFIG_XILINX_LLTEMAC_NATIONAL_DP83865_GMII + u16 RegValue; + int i; + + for (i = 0; i < NATIONAL_DP83865_RETRIES*10; i++) { + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, + NATIONAL_DP83865_STATUS, &RegValue); + if (RegValue & (NATIONAL_DP83865_STATUS_AUTONEGEND + |NATIONAL_DP83865_STATUS_LINK)) + break; + udelay(1 * 100000); + } + + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, + NATIONAL_DP83865_STATUS_AUTONEG, &RegValue); + /* Get current link speed */ + phylinkspeed = (RegValue & NATIONAL_DP83865_LINKSPEED_MASK); + + /* Update TEMAC speed accordingly */ + switch (phylinkspeed) { + case (NATIONAL_DP83865_LINKSPEED_1000M): + _XLlTemac_SetOperatingSpeed(&lp->Emac, 1000); + printk(KERN_INFO "XLlTemac: speed set to 1000Mb/s\n"); + break; + case (NATIONAL_DP83865_LINKSPEED_100M): + _XLlTemac_SetOperatingSpeed(&lp->Emac, 100); + printk(KERN_INFO "XLlTemac: speed set to 100Mb/s\n"); + break; + default: + _XLlTemac_SetOperatingSpeed(&lp->Emac, 10); + printk(KERN_INFO "XLlTemac: speed set to 10Mb/s\n"); + break; + } + + return; + +#elif CONFIG_XILINX_LLTEMAC_MARVELL_88E1111_GMII + /* + * This function is specific to MARVELL 88E1111 PHY chip on + * many Xilinx boards and assumes GMII interface is being used + * by the TEMAC. + */ + +#define MARVELL_88E1111_PHY_SPECIFIC_STATUS_REG_OFFSET 17 +#define MARVELL_88E1111_LINKSPEED_MARK 0xC000 +#define MARVELL_88E1111_LINKSPEED_SHIFT 14 +#define MARVELL_88E1111_LINKSPEED_1000M 0x0002 +#define MARVELL_88E1111_LINKSPEED_100M 0x0001 +#define MARVELL_88E1111_LINKSPEED_10M 0x0000 + u16 RegValue; + + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, + MARVELL_88E1111_PHY_SPECIFIC_STATUS_REG_OFFSET, + &RegValue); + /* Get current link speed */ + phylinkspeed = (RegValue & MARVELL_88E1111_LINKSPEED_MARK) + >> MARVELL_88E1111_LINKSPEED_SHIFT; + + /* Update TEMAC speed accordingly */ + switch (phylinkspeed) { + case (MARVELL_88E1111_LINKSPEED_1000M): + _XLlTemac_SetOperatingSpeed(&lp->Emac, 1000); + printk(KERN_INFO "%s: XLlTemac: speed set to 1000Mb/s\n", + dev->name); + lp->cur_speed = 1000; + break; + case (MARVELL_88E1111_LINKSPEED_100M): + _XLlTemac_SetOperatingSpeed(&lp->Emac, 100); + printk(KERN_INFO "%s: XLlTemac: speed set to 100Mb/s\n", + dev->name); + lp->cur_speed = 100; + break; + case (MARVELL_88E1111_LINKSPEED_10M): + _XLlTemac_SetOperatingSpeed(&lp->Emac, 10); + printk(KERN_INFO "%s: XLlTemac: speed set to 10Mb/s\n", + dev->name); + lp->cur_speed = 10; + break; + default: + _XLlTemac_SetOperatingSpeed(&lp->Emac, 1000); + printk(KERN_INFO "%s: XLlTemac: speed defaults to 1000Mb/s\n", + dev->name); + lp->cur_speed = 1000; + break; + } + +#elif defined CONFIG_XILINX_LLTEMAC_XILINX_1000BASEX + /* + * This function is specific to Xilinx 1000Base-X PHY, + * which only supports 1000Mbps, Full duplex links + */ + + _XLlTemac_SetOperatingSpeed(&lp->Emac, 1000); + printk(KERN_INFO "%s: XLlTemac: speed set to 1000Mb/s\n", dev->name); + lp->cur_speed = 1000; + +#else /* generic PHY, there have been issues with 10Mbit with this code */ + int ret; + int retry_count = 1; + + if (XLlTemac_GetPhysicalInterface(&lp->Emac) == XTE_PHY_TYPE_MII) { + phylinkspeed = 100; + } + else { + phylinkspeed = 1000; + } + + /* + * Try to renegotiate the speed until something sticks + */ + while (phylinkspeed > 1) { + ret = renegotiate_speed(dev, phylinkspeed, FULL_DUPLEX); + /* + * ret == 1 - try it again + * ret == 0 - it worked + * ret < 0 - there was some failure negotiating the speed + */ + if (ret == 0) { + /* it worked, get out of the loop */ + break; + } + + /* it didn't work this time, but it may work if we try again */ + if ((ret == 1) && (retry_count)) { + retry_count--; + printk("trying again...\n"); + continue; + } + /* reset the retry_count, becuase we're about to try a lower speed */ + retry_count = 1; + phylinkspeed /= 10; + } + if (phylinkspeed == 1) { + printk(KERN_INFO "%s: XLlTemac: could not negotiate speed\n", + dev->name); + lp->cur_speed = 0; + + return; + } + + _XLlTemac_SetOperatingSpeed(&lp->Emac, phylinkspeed); + printk(KERN_INFO "%s: XLlTemac: speed set to %dMb/s\n", dev->name, + phylinkspeed); + lp->cur_speed = phylinkspeed; +#endif +} + +/* + * Helper function to reset the underlying hardware. This is called + * when we get into such deep trouble that we don't know how to handle + * otherwise. + */ +static void reset(struct net_device *dev, u32 line_num) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + u32 TxThreshold, TxWaitBound, RxThreshold, RxWaitBound; + u32 Options; + static u32 reset_cnt = 0; + int status; + + printk(KERN_INFO "%s: XLlTemac: resets (#%u) from adapter code line %d\n", + dev->name, ++reset_cnt, line_num); + + /* Shouldn't really be necessary, but shouldn't hurt. */ + netif_stop_queue(dev); + + /* Stop device */ + _XLlTemac_Stop(&lp->Emac); + + /* + * XLlTemac_Reset puts the device back to the default state. We need + * to save all the settings we don't already know, reset, restore + * the settings, and then restart the TEMAC. + */ + Options = XLlTemac_GetOptions(&lp->Emac); + + /* + * Capture the dma coalesce settings (if needed) and reset the + * connected core, dma or fifo + */ + if (XLlTemac_IsDma(&lp->Emac)) { + XLlDma_BdRingGetCoalesce(&XLlDma_mGetRxRing(&lp->Dma), + &RxThreshold, &RxWaitBound); + XLlDma_BdRingGetCoalesce(&XLlDma_mGetTxRing(&lp->Dma), + &TxThreshold, &TxWaitBound); + + XLlDma_Reset(&lp->Dma); + } else { + XLlFifo_Reset(&lp->Fifo); + } + + /* now we can reset the device */ + _XLlTemac_Reset(&lp->Emac, XTE_NORESET_HARD); + + /* Reset on TEMAC also resets PHY. Give it some time to finish negotiation + * before we move on */ + mdelay(2000); + + /* + * The following four functions will return an error if the + * EMAC is already started. We just stopped it by calling + * _XLlTemac_Reset() so we can safely ignore the return values. + */ + (int) _XLlTemac_SetMacAddress(&lp->Emac, dev->dev_addr); + (int) _XLlTemac_SetOptions(&lp->Emac, Options); + (int) _XLlTemac_ClearOptions(&lp->Emac, ~Options); + Options = XLlTemac_GetOptions(&lp->Emac); + printk(KERN_INFO "%s: XLlTemac: Options: 0x%x\n", dev->name, Options); + + phy_setup(lp); + set_mac_speed(lp); + + if (XLlTemac_IsDma(&lp->Emac)) { /* SG DMA mode */ + status = XLlDma_BdRingSetCoalesce(&lp->Dma.RxBdRing, + RxThreshold, RxWaitBound); + status |= XLlDma_BdRingSetCoalesce(&lp->Dma.TxBdRing, + TxThreshold, TxWaitBound); + if (status != XST_SUCCESS) { + /* Print the error, but keep on going as it's not a fatal error. */ + printk(KERN_ERR "%s: XLlTemac: error setting coalesce values (probably out of range). status: %d\n", + dev->name, status); + } + XLlDma_mBdRingIntEnable(&lp->Dma.RxBdRing, dma_rx_int_mask); + XLlDma_mBdRingIntEnable(&lp->Dma.TxBdRing, dma_tx_int_mask); + } else { /* FIFO interrupt mode */ + XLlFifo_IntEnable(&lp->Fifo, XLLF_INT_TC_MASK | + XLLF_INT_RC_MASK | XLLF_INT_RXERROR_MASK | + XLLF_INT_TXERROR_MASK); + } + XLlTemac_IntDisable(&lp->Emac, XTE_INT_ALL_MASK); + + if (lp->deferred_skb) { + dev_kfree_skb_any(lp->deferred_skb); + lp->deferred_skb = NULL; + lp->stats.tx_errors++; + } + + /* + * XLlTemac_Start returns an error when: if configured for + * scatter-gather DMA and a descriptor list has not yet been created + * for the send or receive channel, or if no receive buffer descriptors + * have been initialized. Those are not happening. so ignore the returned + * result checking. + */ + _XLlTemac_Start(&lp->Emac); + + /* We're all ready to go. Start the queue in case it was stopped. */ + netif_wake_queue(dev); +} + +/* + * The PHY registers read here should be standard registers in all PHY chips + */ +static int get_phy_status(struct net_device *dev, DUPLEX * duplex, int *linkup) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + u16 reg; + + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_BMCR, ®); + *duplex = FULL_DUPLEX; + +#ifdef CONFIG_XILINX_LLTEMAC_NATIONAL_DP83865_GMII + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, + NATIONAL_DP83865_STATUS, ®); + *linkup=(reg & NATIONAL_DP83865_STATUS_LINK) != 0; + +#else + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_BMSR, ®); + *linkup = (reg & BMSR_LSTATUS) != 0; +#endif + + return 0; +} + +/* + * This routine is used for two purposes. The first is to keep the + * EMAC's duplex setting in sync with the PHY's. The second is to keep + * the system apprised of the state of the link. Note that this driver + * does not configure the PHY. Either the PHY should be configured for + * auto-negotiation or it should be handled by something like mii-tool. */ +static void poll_gmii(unsigned long data) +{ + struct net_device *dev; + struct net_local *lp; + DUPLEX phy_duplex; + int phy_carrier; + int netif_carrier; + + dev = (struct net_device *) data; + lp = (struct net_local *) netdev_priv(dev); + + /* First, find out what's going on with the PHY. */ + if (get_phy_status(dev, &phy_duplex, &phy_carrier)) { + printk(KERN_ERR "%s: XLlTemac: terminating link monitoring.\n", + dev->name); + return; + } + netif_carrier = netif_carrier_ok(dev) != 0; + if (phy_carrier != netif_carrier) { + if (phy_carrier) { + set_mac_speed(lp); + printk(KERN_INFO + "%s: XLlTemac: PHY Link carrier restored.\n", + dev->name); + netif_carrier_on(dev); + } + else { + printk(KERN_INFO "%s: XLlTemac: PHY Link carrier lost.\n", + dev->name); + netif_carrier_off(dev); + } + } + + /* Set up the timer so we'll get called again in 2 seconds. */ + lp->phy_timer.expires = jiffies + 2 * HZ; + add_timer(&lp->phy_timer); +} + +static irqreturn_t xenet_temac_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct net_local *lp = (struct net_local *) netdev_priv(dev); + + /* + * All we care about here is the RxRject interrupts. Explanation below: + * + * Interrupt Usage Description + * --------- ----------------- + * TxCmplt: Fifo or DMA will have completion interrupts. We'll use + * those and not the TEMAC ones. + * RxFifoOvr: if the RX fifo is overflowing, the last thing we need + * is more interrupts to handle. + * RxRJect: We're keeping stats on rejected packets (we could + * choose not to). + * RxCmplt: Fifo or DMA will have completion interrupts. We'll use + * those and not the TEMAC ones. + * AutoNeg: This driver doesn't make use of the autonegotation + * completion interrupt. + * HardAcsCmplt: This driver just polls the RDY register for this + * information instead of using an interrupt handler. + * CfgWst, CfgRst, + * AfWst, AfRst, + * MiimWst, MiimRst, + * FabrRst: All of these registers indicate when access (read or + * write) to one or other of the Hard Temac Core + * registers is complete. Instead of relying on an + * interrupt context switch to be notified that the + * access is complete, this driver instead polls for the + * status, which, in most cases, should be faster. + */ + XLlTemac_IntClear(&lp->Emac, XTE_INT_ALL_MASK); + + lp->stats.rx_errors++; + lp->stats.rx_crc_errors++; + + + return IRQ_HANDLED; +} + +static void FifoSendHandler(struct net_device *dev); +static void FifoRecvHandler(unsigned long p /*struct net_device *dev*/); + +static DECLARE_TASKLET(FifoRecvBH, FifoRecvHandler, 0); + +static irqreturn_t xenet_fifo_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct net_local *lp = (struct net_local *) netdev_priv(dev); + u32 irq_status; + + unsigned long flags; + + /* + * Need to: + * 1) Read the FIFO IS register + * 2) clear all bits in the FIFO IS register + * 3) loop on each bit in the IS register, and handle each interrupt event + * + */ + irq_status = XLlFifo_IntPending(&lp->Fifo); + XLlFifo_IntClear(&lp->Fifo, irq_status); + while (irq_status) { + if (irq_status & XLLF_INT_RC_MASK) { + /* handle the receive completion */ + struct list_head *cur_lp; + spin_lock_irqsave(&receivedQueueSpin, flags); + list_for_each(cur_lp, &receivedQueue) { + if (cur_lp == &(lp->rcv)) { + break; + } + } + if (cur_lp != &(lp->rcv)) { + list_add_tail(&lp->rcv, &receivedQueue); + XLlFifo_IntDisable(&lp->Fifo, XLLF_INT_ALL_MASK); + tasklet_schedule(&FifoRecvBH); + } + spin_unlock_irqrestore(&receivedQueueSpin, flags); + irq_status &= ~XLLF_INT_RC_MASK; + } else if (irq_status & XLLF_INT_TC_MASK) { + /* handle the transmit completion */ + FifoSendHandler(dev); + irq_status &= ~XLLF_INT_TC_MASK; + } else if (irq_status & XLLF_INT_TXERROR_MASK) { + lp->stats.tx_errors++; + lp->stats.tx_fifo_errors++; + XLlFifo_Reset(&lp->Fifo); + irq_status &= ~XLLF_INT_TXERROR_MASK; + } else if (irq_status & XLLF_INT_RXERROR_MASK) { + lp->stats.rx_errors++; + XLlFifo_Reset(&lp->Fifo); + irq_status &= ~XLLF_INT_RXERROR_MASK; + } else { + /* debug + * if (irq_status == 0) printk("Temac: spurious fifo int\n"); + */ + } + } + + return IRQ_HANDLED; +} + +/* The callback function for completed frames sent in SGDMA mode. */ +static void DmaSendHandlerBH(unsigned long p); +static void DmaRecvHandlerBH(unsigned long p); + +static DECLARE_TASKLET(DmaSendBH, DmaSendHandlerBH, 0); +static DECLARE_TASKLET(DmaRecvBH, DmaRecvHandlerBH, 0); + +static irqreturn_t xenet_dma_rx_interrupt(int irq, void *dev_id) +{ + u32 irq_status; + struct net_device *dev = dev_id; + struct net_local *lp = (struct net_local *) netdev_priv(dev); + struct list_head *cur_lp; + + unsigned long flags; + + /* Read pending interrupts */ + irq_status = XLlDma_mBdRingGetIrq(&lp->Dma.RxBdRing); + + XLlDma_mBdRingAckIrq(&lp->Dma.RxBdRing, irq_status); + + if ((irq_status & XLLDMA_IRQ_ALL_ERR_MASK)) { + XLlDma_Reset(&lp->Dma); + return IRQ_HANDLED; + } + if ((irq_status & (XLLDMA_IRQ_DELAY_MASK | XLLDMA_IRQ_COALESCE_MASK))) { + spin_lock_irqsave(&receivedQueueSpin, flags); + list_for_each(cur_lp, &receivedQueue) { + if (cur_lp == &(lp->rcv)) { + break; + } + } + if (cur_lp != &(lp->rcv)) { + list_add_tail(&lp->rcv, &receivedQueue); + XLlDma_mBdRingIntDisable(&lp->Dma.RxBdRing, + XLLDMA_CR_IRQ_ALL_EN_MASK); + tasklet_schedule(&DmaRecvBH); + } + spin_unlock_irqrestore(&receivedQueueSpin, flags); + } + return IRQ_HANDLED; +} + +static irqreturn_t xenet_dma_tx_interrupt(int irq, void *dev_id) +{ + u32 irq_status; + struct net_device *dev = dev_id; + struct net_local *lp = (struct net_local *) netdev_priv(dev); + struct list_head *cur_lp; + + unsigned long flags; + + /* Read pending interrupts */ + irq_status = XLlDma_mBdRingGetIrq(&(lp->Dma.TxBdRing)); + + XLlDma_mBdRingAckIrq(&(lp->Dma.TxBdRing), irq_status); + + if ((irq_status & XLLDMA_IRQ_ALL_ERR_MASK)) { + XLlDma_Reset(&lp->Dma); + return IRQ_HANDLED; + } + + if ((irq_status & (XLLDMA_IRQ_DELAY_MASK | XLLDMA_IRQ_COALESCE_MASK))) { + spin_lock_irqsave(&sentQueueSpin, flags); + list_for_each(cur_lp, &sentQueue) { + if (cur_lp == &(lp->xmit)) { + break; + } + } + if (cur_lp != &(lp->xmit)) { + list_add_tail(&lp->xmit, &sentQueue); + XLlDma_mBdRingIntDisable(&lp->Dma.TxBdRing, + XLLDMA_CR_IRQ_ALL_EN_MASK); + tasklet_schedule(&DmaSendBH); + } + spin_unlock_irqrestore(&sentQueueSpin, flags); + } + return IRQ_HANDLED; +} + +/* + * Q: + * Why doesn't this linux driver use an interrupt handler for the TEMAC itself? + * + * A: + * Let's take a look at all the possible events that could be signaled by the + * TEMAC core. + * + * possible events: + * Transmit Complete (TxCmplt) [not handled by this driver] + * The TEMAC TxCmplt interrupt status is ignored by software in favor of + * paying attention to the transmit complete status in the connected DMA + * or FIFO core. + * Receive Fifo Overflow (RxFifoOver) [not handled by this driver] + * We have discovered that the overhead of an interrupt context switch + * to attempt to handle this sort of event actually worsens the + * condition, and cuases further dropped packets further increasing the + * time spent in this interrupt handler. + * Receive Frame Rejected (RxRject) [not handled by this driver] + * We could possibly handle this interrupt and gather statistics + * information based on these events that occur. However it is not that + * critical. + * Receive Complete (RxCmplt) [not handled by this driver] + * The TEMAC RxCmplt interrupt status is ignored by software in favor of + * paying attention to the receive complete status in the connected DMA + * or FIFO core. + * Autonegotiaion Complete (AutoNeg) [not handled by this driver] + * Autonegotiation on the TEMAC is a bit complicated, and is handled in + * a way that does not require the use of this interrupt event. + * Hard Temac Core Access Complete (HardAcsCmplt) [not handled by this driver] + * This event really just indicates if there are any events in the TIS + * register. As can be seen below, none of the events from the TIS + * register are handled, so there is no need to handle this event + * either. + * Configuration Write Complete (CfgWst) [not handled by this driver] + * Configuration Read Complete (CfgRst) [not handled by this driver] + * Address Filter Write Complete (AfWst) [not handled by this driver] + * Address Filter Read Complete (AfRst) [not handled by this driver] + * MII Management Write Complete (MiimWst) [not handled by this driver] + * MII Management Read Complete (MiimRst) [not handled by this driver] + * Fabric Read Complete (FabrRst) [not handled by this driver] + * All of the above registers indicate when access (read or write) to + * one or other of the Hard Temac Core registers is complete. Instead of + * relying on an interrupt context switch to be notified that the access + * is complete, this driver instead polls for the status, which, in most + * cases, should be faster. + */ + +static int xenet_open(struct net_device *dev) +{ + struct net_local *lp; + u32 Options; + int irqval = 0; + + /* + * Just to be safe, stop TX queue and the device first. If the device is + * already stopped, an error will be returned. In this case, we don't + * really care. + */ + netif_stop_queue(dev); + lp = (struct net_local *) netdev_priv(dev); + _XLlTemac_Stop(&lp->Emac); + + INIT_LIST_HEAD(&(lp->rcv)); + INIT_LIST_HEAD(&(lp->xmit)); + + /* Set the MAC address each time opened. */ + if (_XLlTemac_SetMacAddress(&lp->Emac, dev->dev_addr) != XST_SUCCESS) { + printk(KERN_ERR "%s: XLlTemac: could not set MAC address.\n", + dev->name); + return -EIO; + } + + /* + * If the device is not configured for polled mode, connect to the + * interrupt controller and enable interrupts. Currently, there + * isn't any code to set polled mode, so this check is probably + * superfluous. + */ + Options = XLlTemac_GetOptions(&lp->Emac); + Options |= XTE_FLOW_CONTROL_OPTION; + /* Enabling jumbo packets shouldn't be a problem if MTU is smaller */ + Options |= XTE_JUMBO_OPTION; + Options |= XTE_TRANSMITTER_ENABLE_OPTION; + Options |= XTE_RECEIVER_ENABLE_OPTION; +#if XTE_AUTOSTRIPPING + Options |= XTE_FCS_STRIP_OPTION; +#endif + + (int) _XLlTemac_SetOptions(&lp->Emac, Options); + (int) _XLlTemac_ClearOptions(&lp->Emac, ~Options); + Options = XLlTemac_GetOptions(&lp->Emac); + printk(KERN_INFO "%s: XLlTemac: Options: 0x%x\n", dev->name, Options); + + /* Just use interrupt driven methods - no polled mode */ + + irqval = request_irq(dev->irq, &xenet_temac_interrupt, 0, dev->name, dev); + if (irqval) { + printk(KERN_ERR + "%s: XLlTemac: could not allocate interrupt %d.\n", + dev->name, dev->irq); + return irqval; + } + if (XLlTemac_IsDma(&lp->Emac)) { + printk(KERN_INFO + "%s: XLlTemac: allocating interrupt %d for dma mode tx.\n", + dev->name, lp->dma_irq_s); + irqval = request_irq(lp->dma_irq_s, + &xenet_dma_tx_interrupt, 0, "xilinx_dma_tx_int", dev); + if (irqval) { + printk(KERN_ERR + "%s: XLlTemac: could not allocate interrupt %d.\n", + dev->name, lp->dma_irq_s); + return irqval; + } + printk(KERN_INFO + "%s: XLlTemac: allocating interrupt %d for dma mode rx.\n", + dev->name, lp->dma_irq_r); + irqval = request_irq(lp->dma_irq_r, + &xenet_dma_rx_interrupt, 0, "xilinx_dma_rx_int", dev); + if (irqval) { + printk(KERN_ERR + "%s: XLlTemac: could not allocate interrupt %d.\n", + dev->name, lp->dma_irq_r); + return irqval; + } + } else { + printk(KERN_INFO + "%s: XLlTemac: allocating interrupt %d for fifo mode.\n", + dev->name, lp->fifo_irq); + /* With the way interrupts are issued on the fifo core, this needs to be + * fast interrupt handler. + */ + irqval = request_irq(lp->fifo_irq, + &xenet_fifo_interrupt, 0, "xilinx_fifo_int", dev); + if (irqval) { + printk(KERN_ERR + "%s: XLlTemac: could not allocate interrupt %d.\n", + dev->name, lp->fifo_irq); + return irqval; + } + } + + /* give the system enough time to establish a link */ + mdelay(2000); + + phy_setup(lp); + set_mac_speed(lp); + + /* Enable interrupts - no polled mode */ + if (XLlTemac_IsFifo(&lp->Emac)) { /* fifo direct interrupt driver mode */ + XLlFifo_IntEnable(&lp->Fifo, XLLF_INT_TC_MASK | + XLLF_INT_RC_MASK | XLLF_INT_RXERROR_MASK | + XLLF_INT_TXERROR_MASK); + } else { /* SG DMA mode */ + XLlDma_mBdRingIntEnable(&lp->Dma.RxBdRing, dma_rx_int_mask); + XLlDma_mBdRingIntEnable(&lp->Dma.TxBdRing, dma_tx_int_mask); + } + /* + * Make sure all temac interrupts are disabled. These + * interrupts are not data flow releated. + */ + XLlTemac_IntDisable(&lp->Emac, XTE_INT_ALL_MASK); + + /* Start TEMAC device */ + _XLlTemac_Start(&lp->Emac); + if (XLlTemac_IsDma(&lp->Emac)) { + u32 threshold_s, timer_s, threshold_r, timer_r; + + XLlDma_BdRingGetCoalesce(&lp->Dma.TxBdRing, &threshold_s, &timer_s); + XLlDma_BdRingGetCoalesce(&lp->Dma.RxBdRing, &threshold_r, &timer_r); + printk(KERN_INFO + "%s: XLlTemac: Send Threshold = %d, Receive Threshold = %d\n", + dev->name, threshold_s, threshold_r); + printk(KERN_INFO + "%s: XLlTemac: Send Wait bound = %d, Receive Wait bound = %d\n", + dev->name, timer_s, timer_r); + if (XLlDma_BdRingStart(&lp->Dma.TxBdRing) == XST_FAILURE) { + printk(KERN_ERR "%s: XLlTemac: could not start dma tx channel\n", dev->name); + return -EIO; + } + if (XLlDma_BdRingStart(&lp->Dma.RxBdRing) == XST_FAILURE) { + printk(KERN_ERR "%s: XLlTemac: could not start dma rx channel\n", dev->name); + return -EIO; + } + } + + /* We're ready to go. */ + netif_start_queue(dev); + + /* Set up the PHY monitoring timer. */ + lp->phy_timer.expires = jiffies + 2 * HZ; + lp->phy_timer.data = (unsigned long) dev; + lp->phy_timer.function = &poll_gmii; + init_timer(&lp->phy_timer); + add_timer(&lp->phy_timer); + return 0; +} + +static int xenet_close(struct net_device *dev) +{ + struct net_local *lp; + unsigned long flags; + + lp = (struct net_local *) netdev_priv(dev); + + /* Shut down the PHY monitoring timer. */ + del_timer_sync(&lp->phy_timer); + + /* Stop Send queue */ + netif_stop_queue(dev); + + /* Now we could stop the device */ + _XLlTemac_Stop(&lp->Emac); + + /* + * Free the interrupt - not polled mode. + */ + free_irq(dev->irq, dev); + if (XLlTemac_IsDma(&lp->Emac)) { + free_irq(lp->dma_irq_s, dev); + free_irq(lp->dma_irq_r, dev); + } else { + free_irq(lp->fifo_irq, dev); + } + + spin_lock_irqsave(&receivedQueueSpin, flags); + list_del(&(lp->rcv)); + spin_unlock_irqrestore(&receivedQueueSpin, flags); + + spin_lock_irqsave(&sentQueueSpin, flags); + list_del(&(lp->xmit)); + spin_unlock_irqrestore(&sentQueueSpin, flags); + + return 0; +} + +static struct net_device_stats *xenet_get_stats(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + + return &lp->stats; +} + +static int descriptor_init(struct net_device *dev); +static void free_descriptor_skb(struct net_device *dev); + +static void xenet_set_multicast_list(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + int i; + u32 Options; + unsigned long flags; + + spin_lock_irqsave(&XTE_spinlock, flags); + XLlTemac_Stop(&lp->Emac); + + Options = XLlTemac_GetOptions(&lp->Emac); + Options &= ~XTE_PROMISC_OPTION; + Options &= ~XTE_MULTICAST_OPTION; + for (i = 0; i < 4; i++) + XLlTemac_MulticastClear(&lp->Emac, i); + + if ((dev->flags & IFF_PROMISC) || + (dev->flags & IFF_ALLMULTI) || + (netdev_mc_count(dev) > 4)) { + Options |= XTE_PROMISC_OPTION; + goto done; + } + + if (dev->flags & IFF_MULTICAST) { + struct netdev_hw_addr *ha; + i = 0; + netdev_for_each_mc_addr(ha, dev) { + XLlTemac_MulticastAdd(&lp->Emac, ha->addr, i++); + } + Options |= XTE_MULTICAST_OPTION; + } + +done: + XLlTemac_SetOptions(&lp->Emac, Options); + + XLlTemac_Start(&lp->Emac); + spin_unlock_irqrestore(&XTE_spinlock, flags); +} + +static int xenet_change_mtu(struct net_device *dev, int new_mtu) +{ + int result; + int device_enable = 0; +#ifdef CONFIG_XILINX_GIGE_VLAN + int head_size = XTE_HDR_VLAN_SIZE; +#else + int head_size = XTE_HDR_SIZE; +#endif + struct net_local *lp = (struct net_local *) netdev_priv(dev); + int max_frame = new_mtu + head_size + XTE_TRL_SIZE; + int min_frame = 1 + head_size + XTE_TRL_SIZE; + +#ifdef CONFIG_PPC + if (netif_running(dev)) { + printk("STOP device first!!!\n"); + return -1; + } +#endif + if (max_frame < min_frame) + return -EINVAL; + + if (max_frame > XTE_MAX_JUMBO_FRAME_SIZE) { + printk(KERN_INFO "Wrong MTU packet size. Use %d size\n", + XTE_JUMBO_MTU); + new_mtu = XTE_JUMBO_MTU; + } + + dev->mtu = new_mtu; /* change mtu in net_device structure */ + + /* stop driver */ + if (netif_running(dev)) { + device_enable = 1; + xenet_close(dev); + } + /* free all created descriptors for previous size */ + free_descriptor_skb(dev); + /* setup new frame size */ + lp->frame_size = dev->mtu + XTE_HDR_SIZE + XTE_TRL_SIZE; + XLlDma_Initialize(&lp->Dma, lp->virt_dma_addr); /* initialize dma */ + + result = descriptor_init(dev); /* create new skb with new size */ + if (result) { + printk(KERN_ERR "Descriptor initialization failed.\n"); + return -EINVAL; + } + + if (device_enable) + xenet_open(dev); /* open the device */ + return 0; +} + +static int xenet_FifoSend(struct sk_buff *skb, struct net_device *dev) +{ + struct net_local *lp; + unsigned long flags, fifo_free_bytes; + int total_frags = skb_shinfo(skb)->nr_frags + 1; + unsigned int total_len; + skb_frag_t *frag; + int i; + void *virt_addr; + + total_len = skb_headlen(skb); + + frag = &skb_shinfo(skb)->frags[0]; + for (i = 1; i < total_frags; i++, frag++) { + total_len += frag->size; + } + + /* The following lock is used to protect TxVacancy, Write + * and TxSetLen sequence which could happen from FifoSendHandler + * or other processor in SMP case. + */ + spin_lock_irqsave(&XTE_tx_spinlock, flags); + lp = (struct net_local *) netdev_priv(dev); + + fifo_free_bytes = XLlFifo_TxVacancy(&lp->Fifo) * 4; + if (fifo_free_bytes < total_len) { + netif_stop_queue(dev); /* stop send queue */ + lp->deferred_skb = skb; /* buffer the sk_buffer and will send + it in interrupt context */ + spin_unlock_irqrestore(&XTE_tx_spinlock, flags); + return 0; + } + + /* Write frame data to FIFO */ + XLlFifo_Write(&lp->Fifo, (void *) skb->data, skb_headlen(skb)); + + frag = &skb_shinfo(skb)->frags[0]; + for (i = 1; i < total_frags; i++, frag++) { + virt_addr = skb_frag_address(frag); + XLlFifo_Write(&lp->Fifo, virt_addr, frag->size); + } + + /* Initiate transmit */ + XLlFifo_TxSetLen(&lp->Fifo, total_len); + lp->stats.tx_bytes += total_len; + spin_unlock_irqrestore(&XTE_tx_spinlock, flags); + + dev_kfree_skb(skb); /* free skb */ + dev->trans_start = jiffies; + return 0; +} + +/* Callback function for completed frames sent in FIFO interrupt driven mode */ +static void FifoSendHandler(struct net_device *dev) +{ + struct net_local *lp; + struct sk_buff *skb; + unsigned long flags; + + spin_lock_irqsave(&XTE_tx_spinlock, flags); + lp = (struct net_local *) netdev_priv(dev); + lp->stats.tx_packets++; + + /*Send out the deferred skb and wake up send queue if a deferred skb exists */ + if (lp->deferred_skb) { + int total_frags; + unsigned int total_len; + unsigned long fifo_free_bytes; + skb_frag_t *frag; + int i; + void *virt_addr; + + skb = lp->deferred_skb; + total_frags = skb_shinfo(skb)->nr_frags + 1; + total_len = skb_headlen(skb); + + frag = &skb_shinfo(skb)->frags[0]; + for (i = 1; i < total_frags; i++, frag++) { + total_len += frag->size; + } + + fifo_free_bytes = XLlFifo_TxVacancy(&lp->Fifo) * 4; + if (fifo_free_bytes < total_len) { + /* If still no room for the deferred packet, return */ + spin_unlock_irqrestore(&XTE_tx_spinlock, flags); + return; + } + + /* Write frame data to FIFO */ + XLlFifo_Write(&lp->Fifo, (void *) skb->data, skb_headlen(skb)); + + frag = &skb_shinfo(skb)->frags[0]; + for (i = 1; i < total_frags; i++, frag++) { + virt_addr = skb_frag_address(frag); + XLlFifo_Write(&lp->Fifo, virt_addr, frag->size); + } + + /* Initiate transmit */ + XLlFifo_TxSetLen(&lp->Fifo, total_len); + + dev_kfree_skb(skb); /* free skb */ + lp->deferred_skb = NULL; + lp->stats.tx_packets++; + lp->stats.tx_bytes += total_len; + dev->trans_start = jiffies; + netif_wake_queue(dev); /* wake up send queue */ + } + spin_unlock_irqrestore(&XTE_tx_spinlock, flags); +} + +#if 0 +/* + * These are used for debugging purposes, left here in case they are useful + * for further debugging + */ +static unsigned int _xenet_tx_csum(struct sk_buff *skb) +{ + unsigned int csum = 0; + long csstart = skb_transport_header(skb) - skb->data; + + if (csstart != skb->len) { + csum = skb_checksum(skb, csstart, skb->len - csstart, 0); + } + + return csum; +} + +static inline unsigned int _xenet_rx_csum(struct sk_buff *skb) +{ + return skb_checksum(skb, 0, skb->len, 0); +} +#endif + +/* + * xenet_DmaSend_internal is an internal use, send routine. + * Any locks that need to be acquired, should be acquired + * prior to calling this routine. + */ +static int xenet_DmaSend_internal(struct sk_buff *skb, struct net_device *dev) +{ + struct net_local *lp; + XLlDma_Bd *bd_ptr; + int result; + int total_frags; + int i; + void *virt_addr; + size_t len; + dma_addr_t phy_addr; + XLlDma_Bd *first_bd_ptr; + XLlDma_Bd *last_bd_ptr; + skb_frag_t *frag; + + lp = (struct net_local *) netdev_priv(dev); + + /* get skb_shinfo(skb)->nr_frags + 1 buffer descriptors */ + total_frags = skb_shinfo(skb)->nr_frags + 1; + + /* stats */ + if (lp->max_frags_in_a_packet < total_frags) { + lp->max_frags_in_a_packet = total_frags; + } + + if (total_frags < XTE_SEND_BD_CNT) { + result = XLlDma_BdRingAlloc(&lp->Dma.TxBdRing, total_frags, + &bd_ptr); + + if (result != XST_SUCCESS) { + netif_stop_queue(dev); /* stop send queue */ + lp->deferred_skb = skb; /* buffer the sk_buffer and will send + it in interrupt context */ + return result; + } + } else { + dev_kfree_skb(skb); + lp->stats.tx_dropped++; + printk(KERN_ERR + "%s: XLlTemac: could not send TX socket buffers (too many fragments).\n", + dev->name); + return XST_FAILURE; + } + + len = skb_headlen(skb); + + /* get the physical address of the header */ + phy_addr = (u32) dma_map_single(dev->dev.parent, skb->data, len, + DMA_TO_DEVICE); + + /* get the header fragment, it's in the skb differently */ + XLlDma_mBdSetBufAddr(bd_ptr, phy_addr); + XLlDma_mBdSetLength(bd_ptr, len); + XLlDma_mBdSetId(bd_ptr, skb); + + /* + * if tx checksum offloading is enabled, when the ethernet stack + * wants us to perform the checksum in hardware, + * skb->ip_summed is CHECKSUM_PARTIAL. Otherwise skb->ip_summed is + * CHECKSUM_NONE, meaning the checksum is already done, or + * CHECKSUM_UNNECESSARY, meaning checksumming is turned off (e.g. + * loopback interface) + * + * skb->csum is an overloaded value. On send, skb->csum is the offset + * into the buffer (skb_transport_header(skb)) to place the csum value. + * On receive this feild gets set to the actual csum value, before it's + * passed up the stack. + * + * When we get here, the ethernet stack above will have already + * computed the pseudoheader csum value and have placed it in the + * TCP/UDP header. + * + * The IP header csum has also already been computed and inserted. + * + * Since the IP header with it's own csum should compute to a null + * csum, it should be ok to include it in the hw csum. If it is decided + * to change this scheme, skb should be examined before dma_map_single() + * is called, which flushes the page from the cpu's cache. + * + * skb->data points to the beginning of the whole packet + * skb_transport_header(skb) points to the beginning of the ip header + * + */ + if (skb->ip_summed == CHECKSUM_PARTIAL) { + + unsigned int csum_start_off = skb_transport_offset(skb); + unsigned int csum_index_off = csum_start_off + skb->csum_offset; + +#if 0 + { + unsigned int csum = _xenet_tx_csum(skb); + + *((unsigned short *) (raw + skb->csum)) = + csum_fold(csum); + BdCsumDisable(bd_ptr); + } +#else + BdCsumEnable(bd_ptr); + BdCsumSetup(bd_ptr, csum_start_off, csum_index_off); + +#endif + lp->tx_hw_csums++; + } + else { + /* + * This routine will do no harm even if hardware checksum capability is + * off. + */ + BdCsumDisable(bd_ptr); + } + + first_bd_ptr = bd_ptr; + last_bd_ptr = bd_ptr; + + frag = &skb_shinfo(skb)->frags[0]; + + for (i = 1; i < total_frags; i++, frag++) { + bd_ptr = XLlDma_mBdRingNext(&lp->Dma.TxBdRing, bd_ptr); + last_bd_ptr = bd_ptr; + + virt_addr = skb_frag_address(frag); + phy_addr = + (u32) dma_map_single(dev->dev.parent, virt_addr, + frag->size, DMA_TO_DEVICE); + + XLlDma_mBdSetBufAddr(bd_ptr, phy_addr); + XLlDma_mBdSetLength(bd_ptr, frag->size); + XLlDma_mBdSetId(bd_ptr, NULL); + BdCsumDisable(bd_ptr); + XLlDma_mBdSetStsCtrl(bd_ptr, 0); + } + + if (first_bd_ptr == last_bd_ptr) { + XLlDma_mBdSetStsCtrl(last_bd_ptr, + XLLDMA_BD_STSCTRL_SOP_MASK | + XLLDMA_BD_STSCTRL_EOP_MASK); + } else { + XLlDma_mBdSetStsCtrl(first_bd_ptr, XLLDMA_BD_STSCTRL_SOP_MASK); + XLlDma_mBdSetStsCtrl(last_bd_ptr, XLLDMA_BD_STSCTRL_EOP_MASK); + } + + + /* Enqueue to HW */ + result = XLlDma_BdRingToHw(&lp->Dma.TxBdRing, total_frags, + first_bd_ptr); + if (result != XST_SUCCESS) { + netif_stop_queue(dev); /* stop send queue */ + dev_kfree_skb(skb); + XLlDma_mBdSetId(first_bd_ptr, NULL); + lp->stats.tx_dropped++; + printk(KERN_ERR + "%s: XLlTemac: could not send commit TX buffer descriptor (%d).\n", + dev->name, result); + reset(dev, __LINE__); + + return XST_FAILURE; + } + + dev->trans_start = jiffies; + + return XST_SUCCESS; +} + +/* The send function for frames sent in DMA mode */ +static int xenet_DmaSend(struct sk_buff *skb, struct net_device *dev) +{ + /* The following spin_lock protects + * SgAlloc, SgCommit sequence, which also exists in DmaSendHandlerBH Bottom + * Half, or triggered by other processor in SMP case. + */ + spin_lock_bh(&XTE_tx_spinlock); + + xenet_DmaSend_internal(skb, dev); + + spin_unlock_bh(&XTE_tx_spinlock); + + return 0; +} + + +static void DmaSendHandlerBH(unsigned long p) +{ + struct net_device *dev; + struct net_local *lp; + XLlDma_Bd *BdPtr, *BdCurPtr; + unsigned long len; + unsigned long flags; + struct sk_buff *skb; + dma_addr_t skb_dma_addr; + int result = XST_SUCCESS; + unsigned int bd_processed, bd_processed_save; + + while (1) { + spin_lock_irqsave(&sentQueueSpin, flags); + if (list_empty(&sentQueue)) { + spin_unlock_irqrestore(&sentQueueSpin, flags); + break; + } + + lp = list_entry(sentQueue.next, struct net_local, xmit); + + list_del_init(&(lp->xmit)); + spin_unlock_irqrestore(&sentQueueSpin, flags); + + spin_lock_irqsave(&XTE_tx_spinlock, flags); + dev = lp->ndev; + bd_processed_save = 0; + while ((bd_processed = + XLlDma_BdRingFromHw(&lp->Dma.TxBdRing, XTE_SEND_BD_CNT, + &BdPtr)) > 0) { + + bd_processed_save = bd_processed; + BdCurPtr = BdPtr; + do { + len = XLlDma_mBdGetLength(BdCurPtr); + skb_dma_addr = (dma_addr_t) XLlDma_mBdGetBufAddr(BdCurPtr); + dma_unmap_single(dev->dev.parent, skb_dma_addr, + len, DMA_TO_DEVICE); + + /* get ptr to skb */ + skb = (struct sk_buff *) + XLlDma_mBdGetId(BdCurPtr); + if (skb) + dev_kfree_skb(skb); + + /* reset BD id */ + XLlDma_mBdSetId(BdCurPtr, NULL); + + lp->stats.tx_bytes += len; + if (XLlDma_mBdGetStsCtrl(BdCurPtr) & XLLDMA_BD_STSCTRL_EOP_MASK) { + lp->stats.tx_packets++; + } + + BdCurPtr = XLlDma_mBdRingNext(&lp->Dma.TxBdRing, BdCurPtr); + bd_processed--; + } while (bd_processed > 0); + + result = XLlDma_BdRingFree(&lp->Dma.TxBdRing, + bd_processed_save, BdPtr); + if (result != XST_SUCCESS) { + printk(KERN_ERR + "%s: XLlDma: BdRingFree() error %d.\n", + dev->name, result); + reset(dev, __LINE__); + spin_unlock_irqrestore(&XTE_tx_spinlock, flags); + return; + } + } + XLlDma_mBdRingIntEnable(&lp->Dma.TxBdRing, dma_tx_int_mask); + + /* Send out the deferred skb if it exists */ + if ((lp->deferred_skb) && bd_processed_save) { + skb = lp->deferred_skb; + lp->deferred_skb = NULL; + + result = xenet_DmaSend_internal(skb, dev); + } + + if (result == XST_SUCCESS) { + netif_wake_queue(dev); /* wake up send queue */ + } + spin_unlock_irqrestore(&XTE_tx_spinlock, flags); + } +} + +static void xenet_tx_timeout(struct net_device *dev) +{ + struct net_local *lp; + unsigned long flags; + + /* + * Make sure that no interrupts come in that could cause reentrancy + * problems in reset. + */ + spin_lock_irqsave(&XTE_tx_spinlock, flags); + + lp = (struct net_local *) netdev_priv(dev); + printk(KERN_ERR + "%s: XLlTemac: exceeded transmit timeout of %lu ms. Resetting emac.\n", + dev->name, TX_TIMEOUT * 1000UL / HZ); + lp->stats.tx_errors++; + + reset(dev, __LINE__); + + spin_unlock_irqrestore(&XTE_tx_spinlock, flags); +} + +/* The callback function for frames received when in FIFO mode. */ +static void FifoRecvHandler(unsigned long p) +{ + struct net_local *lp; + struct sk_buff *skb; + u32 len; + + struct net_device *dev; + unsigned long flags; + spin_lock_irqsave(&receivedQueueSpin, flags); + if (list_empty(&receivedQueue)) { + spin_unlock_irqrestore(&receivedQueueSpin, flags); + return; + } + lp = list_entry(receivedQueue.next, struct net_local, rcv); + + list_del_init(&(lp->rcv)); + spin_unlock_irqrestore(&receivedQueueSpin, flags); + dev = lp->ndev; + + while (XLlFifo_RxOccupancy(&lp->Fifo) != 0) { + + len = XLlFifo_RxGetLen(&lp->Fifo); + + /* + * TODO: Hm this is odd, if we can't allocate the skb, we throw away the next packet. Why? + */ + if (!(skb = /*dev_ */ alloc_skb(len + ALIGNMENT_RECV, GFP_ATOMIC))) { +#define XTE_RX_SINK_BUFFER_SIZE 1024 + static u32 rx_buffer_sink[XTE_RX_SINK_BUFFER_SIZE / sizeof(u32)]; + + /* Couldn't get memory. */ + lp->stats.rx_dropped++; + printk(KERN_ERR + "%s: XLlTemac: could not allocate receive buffer.\n", + dev->name); + + /* consume data in Xilinx TEMAC RX data fifo so it is sync with RX length fifo */ + for (; len > XTE_RX_SINK_BUFFER_SIZE; + len -= XTE_RX_SINK_BUFFER_SIZE) { + XLlFifo_Read(&lp->Fifo, rx_buffer_sink, + XTE_RX_SINK_BUFFER_SIZE); + } + XLlFifo_Read(&lp->Fifo, rx_buffer_sink, len); + break; + } + + /* Read the packet data */ + XLlFifo_Read(&lp->Fifo, skb->data, len); + lp->stats.rx_packets++; + lp->stats.rx_bytes += len; + + skb_put(skb, len); /* Tell the skb how much data we got. */ + skb->dev = dev; /* Fill out required meta-data. */ + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_NONE; + netif_rx(skb); /* Send the packet upstream. */ + } + XLlFifo_IntEnable(&lp->Fifo, XLLF_INT_TC_MASK | XLLF_INT_RC_MASK | + XLLF_INT_RXERROR_MASK | XLLF_INT_TXERROR_MASK); + +} + + +/* + * _xenet_DmaSetupRecvBuffers allocates as many socket buffers (sk_buff's) as it + * can up to the number of free RX buffer descriptors. Then it sets up the RX + * buffer descriptors to DMA into the socket_buffers. + * + * The net_device, dev, indcates on which device to operate for buffer + * descriptor allocation. + */ +static void _xenet_DmaSetupRecvBuffers(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + + int free_bd_count = XLlDma_mBdRingGetFreeCnt(&lp->Dma.RxBdRing); + int num_sk_buffs; + struct sk_buff_head sk_buff_list; + struct sk_buff *new_skb; + u32 new_skb_baddr; + XLlDma_Bd *BdPtr, *BdCurPtr; + u32 align; + int result; + +#if 0 + int align_max = ALIGNMENT_RECV; +#else + int align_max = 0; +#endif + + + skb_queue_head_init(&sk_buff_list); + for (num_sk_buffs = 0; num_sk_buffs < free_bd_count; num_sk_buffs++) { + new_skb = alloc_skb(lp->frame_size + align_max, GFP_ATOMIC); + if (new_skb == NULL) { + break; + } + /* + * I think the XTE_spinlock, and Recv DMA int disabled will protect this + * list as well, so we can use the __ version just fine + */ + __skb_queue_tail(&sk_buff_list, new_skb); + } + if (!num_sk_buffs) { + printk(KERN_ERR "%s: XLlTemac: alloc_skb unsuccessful\n", + dev->name); + return; + } + + /* now we got a bunch o' sk_buffs */ + result = XLlDma_BdRingAlloc(&lp->Dma.RxBdRing, num_sk_buffs, &BdPtr); + if (result != XST_SUCCESS) { + /* we really shouldn't get this */ + skb_queue_purge(&sk_buff_list); + printk(KERN_ERR "%s: XLlDma: BdRingAlloc unsuccessful (%d)\n", + dev->name, result); + reset(dev, __LINE__); + return; + } + + BdCurPtr = BdPtr; + + new_skb = skb_dequeue(&sk_buff_list); + while (new_skb) { + /* make sure we're long-word aligned */ + align = BUFFER_ALIGNRECV(new_skb->data); + if (align) { + skb_reserve(new_skb, align); + } + + /* Get dma handle of skb->data */ + new_skb_baddr = (u32) dma_map_single(dev->dev.parent, + new_skb->data, lp->frame_size, + DMA_FROM_DEVICE); + XLlDma_mBdSetBufAddr(BdCurPtr, new_skb_baddr); + XLlDma_mBdSetLength(BdCurPtr, lp->frame_size); + XLlDma_mBdSetId(BdCurPtr, new_skb); + XLlDma_mBdSetStsCtrl(BdCurPtr, + XLLDMA_BD_STSCTRL_SOP_MASK | + XLLDMA_BD_STSCTRL_EOP_MASK); + + BdCurPtr = XLlDma_mBdRingNext(&lp->Dma.RxBdRing, BdCurPtr); + + new_skb = skb_dequeue(&sk_buff_list); + } + + /* enqueue RxBD with the attached skb buffers such that it is + * ready for frame reception */ + result = XLlDma_BdRingToHw(&lp->Dma.RxBdRing, num_sk_buffs, BdPtr); + if (result != XST_SUCCESS) { + printk(KERN_ERR + "%s: XLlDma: (DmaSetupRecvBuffers) BdRingToHw unsuccessful (%d)\n", + dev->name, result); + skb_queue_purge(&sk_buff_list); + BdCurPtr = BdPtr; + while (num_sk_buffs > 0) { + XLlDma_mBdSetId(BdCurPtr, NULL); + BdCurPtr = XLlDma_mBdRingNext(&lp->Dma.RxBdRing, + BdCurPtr); + num_sk_buffs--; + } + reset(dev, __LINE__); + return; + } +} + +static void DmaRecvHandlerBH(unsigned long p) +{ + struct net_device *dev; + struct net_local *lp; + struct sk_buff *skb; + u32 len, skb_baddr; + int result; + unsigned long flags; + XLlDma_Bd *BdPtr, *BdCurPtr; + unsigned int bd_processed, bd_processed_saved; + + while (1) { + spin_lock_irqsave(&receivedQueueSpin, flags); + if (list_empty(&receivedQueue)) { + spin_unlock_irqrestore(&receivedQueueSpin, flags); + break; + } + lp = list_entry(receivedQueue.next, struct net_local, rcv); + + list_del_init(&(lp->rcv)); + spin_unlock_irqrestore(&receivedQueueSpin, flags); + dev = lp->ndev; + + spin_lock_irqsave(&XTE_rx_spinlock, flags); + if ((bd_processed = + XLlDma_BdRingFromHw(&lp->Dma.RxBdRing, XTE_RECV_BD_CNT, &BdPtr)) > 0) { + + bd_processed_saved = bd_processed; + BdCurPtr = BdPtr; + do { + /* + * Regular length field not updated on rx, + * USR4 updated instead. + */ + len = BdGetRxLen(BdCurPtr); + + /* get ptr to skb */ + skb = (struct sk_buff *) + XLlDma_mBdGetId(BdCurPtr); + + /* get and free up dma handle used by skb->data */ + skb_baddr = (dma_addr_t) XLlDma_mBdGetBufAddr(BdCurPtr); + dma_unmap_single(dev->dev.parent, skb_baddr, + lp->frame_size, + DMA_FROM_DEVICE); + + /* reset ID */ + XLlDma_mBdSetId(BdCurPtr, NULL); + + /* setup received skb and send it upstream */ + skb_put(skb, len); /* Tell the skb how much data we got. */ + skb->dev = dev; + + /* this routine adjusts skb->data to skip the header */ + skb->protocol = eth_type_trans(skb, dev); + + /* default the ip_summed value */ + skb->ip_summed = CHECKSUM_NONE; + + /* if we're doing rx csum offload, set it up */ + if (((lp->local_features & LOCAL_FEATURE_RX_CSUM) != 0) && + (skb->protocol == __constant_htons(ETH_P_IP)) && + (skb->len > 64)) { + unsigned int csum; + + /* + * This hardware only supports proper checksum calculations + * on TCP/UDP packets. + * + * skb->csum is an overloaded value. On send, skb->csum is + * the offset into the buffer (skb_transport_header(skb)) + * to place the csum value. On receive this feild gets set + * to the actual csum value, before it's passed up the stack. + * + * If we set skb->ip_summed to CHECKSUM_COMPLETE, the ethernet + * stack above will compute the pseudoheader csum value and + * add it to the partial checksum already computed (to be + * placed in skb->csum) and verify it. + * + * Setting skb->ip_summed to CHECKSUM_NONE means that the + * cheksum didn't verify and the stack will (re)check it. + * + * Setting skb->ip_summed to CHECKSUM_UNNECESSARY means + * that the cheksum was verified/assumed to be good and the + * stack does not need to (re)check it. + * + * The ethernet stack above will (re)compute the checksum + * under the following conditions: + * 1) skb->ip_summed was set to CHECKSUM_NONE + * 2) skb->len does not match the length of the ethernet + * packet determined by parsing the packet. In this case + * the ethernet stack will assume any prior checksum + * value was miscomputed and throw it away. + * 3) skb->ip_summed was set to CHECKSUM_COMPLETE, skb->csum was + * set, but the result does not check out ok by the + * ethernet stack. + * + * If the TEMAC hardware stripping feature is off, each + * packet will contain an FCS feild which will have been + * computed by the hardware checksum operation. This 4 byte + * FCS value needs to be subtracted back out of the checksum + * value computed by hardware as it's not included in a + * normal ethernet packet checksum. + * + * The minimum transfer packet size over the wire is 64 + * bytes. If the packet is sent as exactly 64 bytes, then + * it probably contains some random padding bytes. It's + * somewhat difficult to determine the actual length of the + * real packet data, so we just let the stack recheck the + * checksum for us. + * + * After the call to eth_type_trans(), the following holds + * true: + * skb->data points to the beginning of the ip header + */ + csum = BdCsumGet(BdCurPtr); + +#if ! XTE_AUTOSTRIPPING + if (!lp->stripping) { + /* take off the FCS */ + u16 *data; + + /* FCS is 4 bytes */ + skb_put(skb, -4); + + data = (u16 *) (&skb-> + data[skb->len]); + + /* subtract out the FCS from the csum value */ + csum = csum_sub(csum, *data /* & 0xffff */); + data++; + csum = csum_sub(csum, *data /* & 0xffff */); + } +#endif + skb->csum = csum; + skb->ip_summed = CHECKSUM_COMPLETE; + + lp->rx_hw_csums++; + } + + lp->stats.rx_packets++; + lp->stats.rx_bytes += len; + netif_rx(skb); /* Send the packet upstream. */ + + BdCurPtr = + XLlDma_mBdRingNext(&lp->Dma.RxBdRing, + BdCurPtr); + bd_processed--; + } while (bd_processed > 0); + + /* give the descriptor back to the driver */ + result = XLlDma_BdRingFree(&lp->Dma.RxBdRing, + bd_processed_saved, BdPtr); + if (result != XST_SUCCESS) { + printk(KERN_ERR + "%s: XLlDma: BdRingFree unsuccessful (%d)\n", + dev->name, result); + reset(dev, __LINE__); + spin_unlock_irqrestore(&XTE_rx_spinlock, flags); + return; + } + + _xenet_DmaSetupRecvBuffers(dev); + } + XLlDma_mBdRingIntEnable(&lp->Dma.RxBdRing, dma_rx_int_mask); + spin_unlock_irqrestore(&XTE_rx_spinlock, flags); + } +} + +static int descriptor_init(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + int recvsize, sendsize; + int dftsize; + u32 *recvpoolptr, *sendpoolptr; + void *recvpoolphy, *sendpoolphy; + int result; + +/* + * Buffer Descriptr + * word byte description + * 0 0h next ptr + * 1 4h buffer addr + * 2 8h buffer len + * 3 ch sts/ctrl | app data (0) [tx csum enable (bit 31 LSB)] + * 4 10h app data (1) [tx csum begin (bits 0-15 MSB) | csum insert (bits 16-31 LSB)] + * 5 14h app data (2) [tx csum seed (bits 16-31 LSB)] + * 6 18h app data (3) [rx raw csum (bits 16-31 LSB)] + * 7 1ch app data (4) [rx recv length (bits 18-31 LSB)] + */ +#if 0 + int XferType = XDMAV3_DMACR_TYPE_BFBURST_MASK; + int XferWidth = XDMAV3_DMACR_DSIZE_64_MASK; +#endif + + /* calc size of descriptor space pool; alloc from non-cached memory */ + dftsize = XLlDma_mBdRingMemCalc(ALIGNMENT_BD, + XTE_RECV_BD_CNT + XTE_SEND_BD_CNT); + printk(KERN_INFO "XLlTemac: buffer descriptor size: %d (0x%0x)\n", + dftsize, dftsize); + +#if BD_IN_BRAM == 0 + /* + * Allow buffer descriptors to be cached. + * Old method w/cache on buffer descriptors disabled: + * lp->desc_space = dma_alloc_coherent(NULL, dftsize, + * &lp->desc_space_handle, GFP_KERNEL); + * (note if going back to dma_alloc_coherent() the CACHE macros in + * xenv_linux.h need to be disabled. + */ + + printk(KERN_INFO "XLlTemac: Allocating DMA descriptors with kmalloc"); + lp->desc_space = kmalloc(dftsize, GFP_KERNEL); + lp->desc_space_handle = (dma_addr_t) page_to_phys(virt_to_page(lp->desc_space)); +#else + printk(KERN_INFO "XLlTemac: Allocating DMA descriptors in Block Ram"); + lp->desc_space_handle = BRAM_BASEADDR; + lp->desc_space = ioremap(lp->desc_space_handle, dftsize); +#endif + if (lp->desc_space == NULL) + return -1; + + lp->desc_space_size = dftsize; + + printk(KERN_INFO + "XLlTemac: (buffer_descriptor_init) phy: 0x%x, virt: 0x%x, size: 0x%x\n", + lp->desc_space_handle, (unsigned int) lp->desc_space, + lp->desc_space_size); + + /* calc size of send and recv descriptor space */ + recvsize = XLlDma_mBdRingMemCalc(ALIGNMENT_BD, XTE_RECV_BD_CNT); + sendsize = XLlDma_mBdRingMemCalc(ALIGNMENT_BD, XTE_SEND_BD_CNT); + + recvpoolptr = lp->desc_space; + sendpoolptr = (void *) ((u32) lp->desc_space + recvsize); + + recvpoolphy = (void *) lp->desc_space_handle; + sendpoolphy = (void *) ((u32) lp->desc_space_handle + recvsize); + + result = XLlDma_BdRingCreate(&lp->Dma.RxBdRing, (u32) recvpoolphy, + (u32) recvpoolptr, ALIGNMENT_BD, + XTE_RECV_BD_CNT); + if (result != XST_SUCCESS) { + printk(KERN_ERR "XLlTemac: DMA Ring Create (RECV). Error: %d\n", result); + return -EIO; + } + + result = XLlDma_BdRingCreate(&lp->Dma.TxBdRing, (u32) sendpoolphy, + (u32) sendpoolptr, ALIGNMENT_BD, + XTE_SEND_BD_CNT); + if (result != XST_SUCCESS) { + printk(KERN_ERR "XLlTemac: DMA Ring Create (SEND). Error: %d\n", result); + return -EIO; + } + + _xenet_DmaSetupRecvBuffers(dev); + return 0; +} + +static void free_descriptor_skb(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + XLlDma_Bd *BdPtr; + struct sk_buff *skb; + dma_addr_t skb_dma_addr; + u32 len, i; + + /* Unmap and free skb's allocated and mapped in descriptor_init() */ + + /* Get the virtual address of the 1st BD in the DMA RX BD ring */ + BdPtr = (XLlDma_Bd *) lp->Dma.RxBdRing.FirstBdAddr; + + for (i = 0; i < XTE_RECV_BD_CNT; i++) { + skb = (struct sk_buff *) XLlDma_mBdGetId(BdPtr); + if (skb) { + skb_dma_addr = (dma_addr_t) XLlDma_mBdGetBufAddr(BdPtr); + dma_unmap_single(dev->dev.parent, skb_dma_addr, + lp->frame_size, DMA_FROM_DEVICE); + dev_kfree_skb(skb); + } + /* find the next BD in the DMA RX BD ring */ + BdPtr = XLlDma_mBdRingNext(&lp->Dma.RxBdRing, BdPtr); + } + + /* Unmap and free TX skb's that have not had a chance to be freed + * in DmaSendHandlerBH(). This could happen when TX Threshold is larger + * than 1 and TX waitbound is 0 + */ + + /* Get the virtual address of the 1st BD in the DMA TX BD ring */ + BdPtr = (XLlDma_Bd *) lp->Dma.TxBdRing.FirstBdAddr; + + for (i = 0; i < XTE_SEND_BD_CNT; i++) { + skb = (struct sk_buff *) XLlDma_mBdGetId(BdPtr); + if (skb) { + skb_dma_addr = (dma_addr_t) XLlDma_mBdGetBufAddr(BdPtr); + len = XLlDma_mBdGetLength(BdPtr); + dma_unmap_single(dev->dev.parent, skb_dma_addr, len, + DMA_TO_DEVICE); + dev_kfree_skb(skb); + } + /* find the next BD in the DMA TX BD ring */ + BdPtr = XLlDma_mBdRingNext(&lp->Dma.TxBdRing, BdPtr); + } + +#if BD_IN_BRAM == 0 + kfree(lp->desc_space); +/* this is old approach which was removed */ +/* dma_free_coherent(NULL, + lp->desc_space_size, + lp->desc_space, lp->desc_space_handle); */ +#else + iounmap(lp->desc_space); +#endif +} + +static int +xenet_ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + u32 mac_options; + u32 threshold, timer; + u16 gmii_cmd, gmii_status, gmii_advControl; + + memset(ecmd, 0, sizeof(struct ethtool_cmd)); + + mac_options = XLlTemac_GetOptions(&(lp->Emac)); + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_BMCR, &gmii_cmd); + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_BMSR, &gmii_status); + + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_ADVERTISE, &gmii_advControl); + + ecmd->duplex = DUPLEX_FULL; + + ecmd->supported |= SUPPORTED_MII; + + ecmd->port = PORT_MII; + + ecmd->speed = lp->cur_speed; + + if (gmii_status & BMSR_ANEGCAPABLE) { + ecmd->supported |= SUPPORTED_Autoneg; + } + if (gmii_status & BMSR_ANEGCOMPLETE) { + ecmd->autoneg = AUTONEG_ENABLE; + ecmd->advertising |= ADVERTISED_Autoneg; + } + else { + ecmd->autoneg = AUTONEG_DISABLE; + } + ecmd->phy_address = lp->Emac.Config.BaseAddress; + ecmd->transceiver = XCVR_INTERNAL; + if (XLlTemac_IsDma(&lp->Emac)) { + /* get TX threshold */ + + XLlDma_BdRingGetCoalesce(&lp->Dma.TxBdRing, &threshold, &timer); + ecmd->maxtxpkt = threshold; + + /* get RX threshold */ + XLlDma_BdRingGetCoalesce(&lp->Dma.RxBdRing, &threshold, &timer); + ecmd->maxrxpkt = threshold; + } + + ecmd->supported |= SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg; + + return 0; +} + +static int +xenet_ethtool_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + + if ((ecmd->duplex != DUPLEX_FULL) || + (ecmd->transceiver != XCVR_INTERNAL) || + (ecmd->phy_address && + (ecmd->phy_address != lp->Emac.Config.BaseAddress))) { + return -EOPNOTSUPP; + } + + if ((ecmd->speed != 1000) && (ecmd->speed != 100) && + (ecmd->speed != 10)) { + printk(KERN_ERR + "%s: XLlTemac: xenet_ethtool_set_settings speed not supported: %d\n", + dev->name, ecmd->speed); + return -EOPNOTSUPP; + } + + if (ecmd->speed != lp->cur_speed) { + renegotiate_speed(dev, ecmd->speed, FULL_DUPLEX); + _XLlTemac_SetOperatingSpeed(&lp->Emac, ecmd->speed); + lp->cur_speed = ecmd->speed; + } + return 0; +} + +static int +xenet_ethtool_get_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + u32 threshold, waitbound; + + memset(ec, 0, sizeof(struct ethtool_coalesce)); + + if (!(XLlTemac_IsDma(&lp->Emac))) { + return -EIO; + } + + XLlDma_BdRingGetCoalesce(&lp->Dma.RxBdRing, &threshold, &waitbound); + ec->rx_max_coalesced_frames = threshold; + ec->rx_coalesce_usecs = waitbound; + + XLlDma_BdRingGetCoalesce(&lp->Dma.TxBdRing, &threshold, &waitbound); + ec->tx_max_coalesced_frames = threshold; + ec->tx_coalesce_usecs = waitbound; + + return 0; +} + +#if 0 +static void disp_bd_ring(XLlDma_BdRing *bd_ring) +{ + int num_bds = bd_ring->AllCnt; + u32 *cur_bd_ptr = (u32 *) bd_ring->FirstBdAddr; + int idx; + + printk("ChanBase: %p\n", (void *) bd_ring->ChanBase); + printk("FirstBdPhysAddr: %p\n", (void *) bd_ring->FirstBdPhysAddr); + printk("FirstBdAddr: %p\n", (void *) bd_ring->FirstBdAddr); + printk("LastBdAddr: %p\n", (void *) bd_ring->LastBdAddr); + printk("Length: %d (0x%0x)\n", bd_ring->Length, bd_ring->Length); + printk("RunState: %d (0x%0x)\n", bd_ring->RunState, bd_ring->RunState); + printk("Separation: %d (0x%0x)\n", bd_ring->Separation, + bd_ring->Separation); + printk("BD Count: %d\n", bd_ring->AllCnt); + + printk("\n"); + + printk("FreeHead: %p\n", (void *) bd_ring->FreeHead); + printk("PreHead: %p\n", (void *) bd_ring->PreHead); + printk("HwHead: %p\n", (void *) bd_ring->HwHead); + printk("HwTail: %p\n", (void *) bd_ring->HwTail); + printk("PostHead: %p\n", (void *) bd_ring->PostHead); + printk("BdaRestart: %p\n", (void *) bd_ring->BdaRestart); + + printk("Ring Contents:\n"); +/* + * Buffer Descriptr + * word byte description + * 0 0h next ptr + * 1 4h buffer addr + * 2 8h buffer len + * 3 ch sts/ctrl | app data (0) [tx csum enable (bit 31 LSB)] + * 4 10h app data (1) [tx csum begin (bits 0-15 MSB) | csum insert (bits 16-31 LSB)] + * 5 14h app data (2) [tx csum seed (bits 16-31 LSB)] + * 6 18h app data (3) [rx raw csum (bits 16-31 LSB)] + * 7 1ch app data (4) [rx recv length (bits 18-31 LSB)] + * 8 20h sw app data (0) [id] + */ + printk("Idx NextBD BuffAddr Length CTL/CSE CSUM B/I CSUMSeed Raw CSUM RecvLen ID\n"); + printk("--- -------- -------- -------- -------- -------- -------- -------- -------- --------\n"); + + for (idx = 0; idx < num_bds; idx++) { + printk("%3d %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", + idx, + cur_bd_ptr[XLLDMA_BD_NDESC_OFFSET / sizeof(*cur_bd_ptr)], + cur_bd_ptr[XLLDMA_BD_BUFA_OFFSET / sizeof(*cur_bd_ptr)], + cur_bd_ptr[XLLDMA_BD_BUFL_OFFSET / sizeof(*cur_bd_ptr)], + cur_bd_ptr[XLLDMA_BD_STSCTRL_USR0_OFFSET / + sizeof(*cur_bd_ptr)], + cur_bd_ptr[XLLDMA_BD_USR1_OFFSET / sizeof(*cur_bd_ptr)], + cur_bd_ptr[XLLDMA_BD_USR2_OFFSET / sizeof(*cur_bd_ptr)], + cur_bd_ptr[XLLDMA_BD_USR3_OFFSET / sizeof(*cur_bd_ptr)], + cur_bd_ptr[XLLDMA_BD_USR4_OFFSET / sizeof(*cur_bd_ptr)], + cur_bd_ptr[XLLDMA_BD_ID_OFFSET / sizeof(*cur_bd_ptr)]); + + cur_bd_ptr += bd_ring->Separation / sizeof(int); + } + printk("--------------------------------------- Done ---------------------------------------\n"); +} +#endif + +static int +xenet_ethtool_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) +{ + int ret; + struct net_local *lp; + + lp = (struct net_local *) netdev_priv(dev); + + if (!(XLlTemac_IsDma(&lp->Emac))) { + return -EIO; + } + + if (ec->rx_coalesce_usecs == 0) { + ec->rx_coalesce_usecs = 1; + dma_rx_int_mask = XLLDMA_CR_IRQ_ALL_EN_MASK & ~XLLDMA_CR_IRQ_DELAY_EN_MASK; + } + if ((ret = XLlDma_BdRingSetCoalesce(&lp->Dma.RxBdRing, + (u16) (ec->rx_max_coalesced_frames), + (u16) (ec->rx_coalesce_usecs))) != XST_SUCCESS) { + printk(KERN_ERR "%s: XLlDma: BdRingSetCoalesce error %d\n", + dev->name, ret); + return -EIO; + } + XLlDma_mBdRingIntEnable(&lp->Dma.RxBdRing, dma_rx_int_mask); + + if (ec->tx_coalesce_usecs == 0) { + ec->tx_coalesce_usecs = 1; + dma_tx_int_mask = XLLDMA_CR_IRQ_ALL_EN_MASK & ~XLLDMA_CR_IRQ_DELAY_EN_MASK; + } + if ((ret = XLlDma_BdRingSetCoalesce(&lp->Dma.TxBdRing, + (u16) (ec->tx_max_coalesced_frames), + (u16) (ec->tx_coalesce_usecs))) != XST_SUCCESS) { + printk(KERN_ERR "%s: XLlDma: BdRingSetCoalesce error %d\n", + dev->name, ret); + return -EIO; + } + XLlDma_mBdRingIntEnable(&lp->Dma.TxBdRing, dma_tx_int_mask); + + return 0; +} + +static void +xenet_ethtool_get_ringparam(struct net_device *dev, + struct ethtool_ringparam *erp) +{ + memset(erp, 0, sizeof(struct ethtool_ringparam)); + + erp->rx_max_pending = XTE_RECV_BD_CNT; + erp->tx_max_pending = XTE_SEND_BD_CNT; + erp->rx_pending = XTE_RECV_BD_CNT; + erp->tx_pending = XTE_SEND_BD_CNT; +} + +static void +xenet_ethtool_get_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *epp) +{ + u32 Options; + u16 gmii_status; + struct net_local *lp = netdev_priv(dev); + + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_BMSR, &gmii_status); + + /* I suspect that the expected value is that autonegotiation is + * enabled, not completed. + * As seen in xenet_do_ethtool_ioctl() */ + if (gmii_status & BMSR_ANEGCOMPLETE) { + epp->autoneg = AUTONEG_ENABLE; + } + else { + epp->autoneg = AUTONEG_DISABLE; + } + + Options = XLlTemac_GetOptions(&lp->Emac); + if (Options & XTE_FLOW_CONTROL_OPTION) { + epp->rx_pause = 1; + epp->tx_pause = 1; + } + else { + epp->rx_pause = 0; + epp->tx_pause = 0; + } +} + +#if 0 +static u32 +xenet_ethtool_get_rx_csum(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + u32 retval; + + retval = (lp->local_features & LOCAL_FEATURE_RX_CSUM) != 0; + + return retval; +} + +static int +xenet_ethtool_set_rx_csum(struct net_device *dev, u32 onoff) +{ + struct net_local *lp = netdev_priv(dev); + + if (onoff) { + if (XLlTemac_IsRxCsum(&lp->Emac) == TRUE) { + lp->local_features |= + LOCAL_FEATURE_RX_CSUM; + } + } + else { + lp->local_features &= ~LOCAL_FEATURE_RX_CSUM; + } + + return 0; +} + +static u32 +xenet_ethtool_get_tx_csum(struct net_device *dev) +{ + u32 retval; + + retval = (dev->features & NETIF_F_IP_CSUM) != 0; + return retval; +} + +static int +xenet_ethtool_set_tx_csum(struct net_device *dev, u32 onoff) +{ + struct net_local *lp = netdev_priv(dev); + + if (onoff) { + if (XLlTemac_IsTxCsum(&lp->Emac) == TRUE) { + dev->features |= NETIF_F_IP_CSUM; + } + } + else { + dev->features &= ~NETIF_F_IP_CSUM; + } + + return 0; +} + +static u32 +xenet_ethtool_get_sg(struct net_device *dev) +{ + u32 retval; + + retval = (dev->features & NETIF_F_SG) != 0; + + return retval; +} + +static int +xenet_ethtool_set_sg(struct net_device *dev, u32 onoff) +{ + struct net_local *lp = netdev_priv(dev); + + if (onoff) { + if (XLlTemac_IsDma(&lp->Emac)) { + dev->features |= + NETIF_F_SG | NETIF_F_FRAGLIST; + } + } + else { + dev->features &= + ~(NETIF_F_SG | NETIF_F_FRAGLIST); + } + + return 0; +} +#endif +static void +xenet_ethtool_get_strings(struct net_device *dev, u32 stringset, u8 *strings) +{ + *strings = 0; + + switch (stringset) { + case ETH_SS_STATS: + memcpy(strings, + &xenet_ethtool_gstrings_stats, + sizeof(xenet_ethtool_gstrings_stats)); + + break; + + default: + break; + } +} + +static void +xenet_ethtool_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct net_local *lp = netdev_priv(dev); + + data[0] = lp->stats.tx_packets; + data[1] = lp->stats.tx_dropped; + data[2] = lp->stats.tx_errors; + data[3] = lp->stats.tx_fifo_errors; + data[4] = lp->stats.rx_packets; + data[5] = lp->stats.rx_dropped; + data[6] = lp->stats.rx_errors; + data[7] = lp->stats.rx_fifo_errors; + data[8] = lp->stats.rx_crc_errors; + data[9] = lp->max_frags_in_a_packet; + data[10] = lp->tx_hw_csums; + data[11] = lp->rx_hw_csums; +} + +static int +xenet_ethtool_get_sset_count(struct net_device *netdev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return XENET_STATS_LEN; + default: + return -EOPNOTSUPP; + } + + return 0; +} + + +#define EMAC_REGS_N 32 +struct mac_regsDump { + struct ethtool_regs hd; + u16 data[EMAC_REGS_N]; +}; + +static int +xenet_ethtool_get_regs_len(struct net_device *dev) +{ + return (sizeof(u16) * EMAC_REGS_N); +} + +static void +xenet_ethtool_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *ret) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + struct mac_regsDump *dump = (struct mac_regsDump *) regs; + int i; + + dump->hd.version = 0; + dump->hd.len = sizeof(dump->data); + memset(dump->data, 0, sizeof(dump->data)); + + for (i = 0; i < EMAC_REGS_N; i++) { + _XLlTemac_PhyRead(&lp->Emac, lp->gmii_addr, i, &(dump->data[i])); + } + + *(int *) ret = 0; +} + +static void +xenet_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *ed) +{ + memset(ed, 0, sizeof(struct ethtool_drvinfo)); + strncpy(ed->driver, DRIVER_NAME, sizeof(ed->driver) - 1); + strncpy(ed->version, DRIVER_VERSION, sizeof(ed->version) - 1); + /* Also tell how much memory is needed for dumping register values */ + ed->regdump_len = sizeof(u16) * EMAC_REGS_N; + ed->n_stats = XENET_STATS_LEN; +} + +/* + * xenet_do_ethtool_ioctl: + * DEPRECATED + */ +static int xenet_do_ethtool_ioctl(struct net_device *dev, struct ifreq *rq) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + struct ethtool_cmd ecmd; + struct ethtool_coalesce eco; + struct ethtool_drvinfo edrv; + struct ethtool_ringparam erp; + struct ethtool_pauseparam epp; + struct mac_regsDump regs; + int ret = -EOPNOTSUPP; + u32 Options; + + if (copy_from_user(&ecmd, rq->ifr_data, sizeof(ecmd))) + return -EFAULT; + switch (ecmd.cmd) { + case ETHTOOL_GSET: /* Get setting. No command option needed w/ ethtool */ + ret = xenet_ethtool_get_settings(dev, &ecmd); + if (ret < 0) + return -EIO; + if (copy_to_user(rq->ifr_data, &ecmd, sizeof(ecmd))) + return -EFAULT; + ret = 0; + break; + case ETHTOOL_SSET: /* Change setting. Use "-s" command option w/ ethtool */ + ret = xenet_ethtool_set_settings(dev, &ecmd); + break; + case ETHTOOL_GPAUSEPARAM: /* Get pause parameter information. Use "-a" w/ ethtool */ + ret = xenet_ethtool_get_settings(dev, &ecmd); + if (ret < 0) + return ret; + epp.cmd = ecmd.cmd; + epp.autoneg = ecmd.autoneg; + Options = XLlTemac_GetOptions(&lp->Emac); + if (Options & XTE_FCS_INSERT_OPTION) { + epp.rx_pause = 1; + epp.tx_pause = 1; + } + else { + epp.rx_pause = 0; + epp.tx_pause = 0; + } + if (copy_to_user + (rq->ifr_data, &epp, sizeof(struct ethtool_pauseparam))) + return -EFAULT; + ret = 0; + break; + case ETHTOOL_SPAUSEPARAM: /* Set pause parameter. Use "-A" w/ ethtool */ + return -EOPNOTSUPP; /* TODO: To support in next version */ + case ETHTOOL_GRXCSUM:{ /* Get rx csum offload info. Use "-k" w/ ethtool */ + struct ethtool_value edata = { ETHTOOL_GRXCSUM }; + + edata.data = + (lp->local_features & LOCAL_FEATURE_RX_CSUM) != + 0; + if (copy_to_user(rq->ifr_data, &edata, sizeof(edata))) + return -EFAULT; + ret = 0; + break; + } + case ETHTOOL_SRXCSUM:{ /* Set rx csum offload info. Use "-K" w/ ethtool */ + struct ethtool_value edata; + + if (copy_from_user(&edata, rq->ifr_data, sizeof(edata))) + return -EFAULT; + + if (edata.data) { + if (XLlTemac_IsRxCsum(&lp->Emac) == TRUE) { + lp->local_features |= + LOCAL_FEATURE_RX_CSUM; + } + } + else { + lp->local_features &= ~LOCAL_FEATURE_RX_CSUM; + } + + ret = 0; + break; + } + case ETHTOOL_GTXCSUM:{ /* Get tx csum offload info. Use "-k" w/ ethtool */ + struct ethtool_value edata = { ETHTOOL_GTXCSUM }; + + edata.data = (dev->features & NETIF_F_IP_CSUM) != 0; + if (copy_to_user(rq->ifr_data, &edata, sizeof(edata))) + return -EFAULT; + ret = 0; + break; + } + case ETHTOOL_STXCSUM:{ /* Set tx csum offload info. Use "-K" w/ ethtool */ + struct ethtool_value edata; + + if (copy_from_user(&edata, rq->ifr_data, sizeof(edata))) + return -EFAULT; + + if (edata.data) { + if (XLlTemac_IsTxCsum(&lp->Emac) == TRUE) { + dev->features |= NETIF_F_IP_CSUM; + } + } + else { + dev->features &= ~NETIF_F_IP_CSUM; + } + + ret = 0; + break; + } + case ETHTOOL_GSG:{ /* Get ScatterGather info. Use "-k" w/ ethtool */ + struct ethtool_value edata = { ETHTOOL_GSG }; + + edata.data = (dev->features & NETIF_F_SG) != 0; + if (copy_to_user(rq->ifr_data, &edata, sizeof(edata))) + return -EFAULT; + ret = 0; + break; + } + case ETHTOOL_SSG:{ /* Set ScatterGather info. Use "-K" w/ ethtool */ + struct ethtool_value edata; + + if (copy_from_user(&edata, rq->ifr_data, sizeof(edata))) + return -EFAULT; + + if (edata.data) { + if (XLlTemac_IsDma(&lp->Emac)) { + dev->features |= + NETIF_F_SG | NETIF_F_FRAGLIST; + } + } + else { + dev->features &= + ~(NETIF_F_SG | NETIF_F_FRAGLIST); + } + + ret = 0; + break; + } + case ETHTOOL_GCOALESCE: /* Get coalescing info. Use "-c" w/ ethtool */ + if (!(XLlTemac_IsDma(&lp->Emac))) + break; + eco.cmd = ecmd.cmd; + ret = xenet_ethtool_get_coalesce(dev, &eco); + if (ret < 0) { + return -EIO; + } + if (copy_to_user + (rq->ifr_data, &eco, sizeof(struct ethtool_coalesce))) { + return -EFAULT; + } + ret = 0; + break; + case ETHTOOL_SCOALESCE: /* Set coalescing info. Use "-C" w/ ethtool */ + if (!(XLlTemac_IsDma(&lp->Emac))) + break; + if (copy_from_user + (&eco, rq->ifr_data, sizeof(struct ethtool_coalesce))) + return -EFAULT; + ret = xenet_ethtool_set_coalesce(dev, &eco); + break; + case ETHTOOL_GDRVINFO: /* Get driver information. Use "-i" w/ ethtool */ + edrv.cmd = edrv.cmd; + xenet_ethtool_get_drvinfo(dev, &edrv); + edrv.n_stats = XENET_STATS_LEN; + if (copy_to_user + (rq->ifr_data, &edrv, sizeof(struct ethtool_drvinfo))) { + return -EFAULT; + } + ret = 0; + break; + case ETHTOOL_GREGS: /* Get register values. Use "-d" with ethtool */ + regs.hd.cmd = edrv.cmd; + xenet_ethtool_get_regs(dev, &(regs.hd), &ret); + if (ret < 0) { + return ret; + } + if (copy_to_user + (rq->ifr_data, ®s, sizeof(struct mac_regsDump))) { + return -EFAULT; + } + ret = 0; + break; + case ETHTOOL_GRINGPARAM: /* Get RX/TX ring parameters. Use "-g" w/ ethtool */ + erp.cmd = edrv.cmd; + xenet_ethtool_get_ringparam(dev, &(erp)); + if (copy_to_user + (rq->ifr_data, &erp, sizeof(struct ethtool_ringparam))) { + return -EFAULT; + } + ret = 0; + break; + case ETHTOOL_NWAY_RST: /* Restart auto negotiation if enabled. Use "-r" w/ ethtool */ + return -EOPNOTSUPP; /* TODO: To support in next version */ + case ETHTOOL_GSTRINGS:{ + struct ethtool_gstrings gstrings = { ETHTOOL_GSTRINGS }; + void __user *addr = rq->ifr_data; + char *strings = NULL; + + if (copy_from_user(&gstrings, addr, sizeof(gstrings))) { + return -EFAULT; + } + switch (gstrings.string_set) { + case ETH_SS_STATS: + gstrings.len = XENET_STATS_LEN; + strings = *xenet_ethtool_gstrings_stats; + break; + default: + return -EOPNOTSUPP; + } + if (copy_to_user(addr, &gstrings, sizeof(gstrings))) { + return -EFAULT; + } + addr += offsetof(struct ethtool_gstrings, data); + if (copy_to_user + (addr, strings, gstrings.len * ETH_GSTRING_LEN)) { + return -EFAULT; + } + ret = 0; + break; + } + case ETHTOOL_GSTATS:{ + struct { + struct ethtool_stats cmd; + uint64_t data[XENET_STATS_LEN]; + } stats = { { + ETHTOOL_GSTATS, XENET_STATS_LEN}}; + + stats.data[0] = lp->stats.tx_packets; + stats.data[1] = lp->stats.tx_dropped; + stats.data[2] = lp->stats.tx_errors; + stats.data[3] = lp->stats.tx_fifo_errors; + stats.data[4] = lp->stats.rx_packets; + stats.data[5] = lp->stats.rx_dropped; + stats.data[6] = lp->stats.rx_errors; + stats.data[7] = lp->stats.rx_fifo_errors; + stats.data[8] = lp->stats.rx_crc_errors; + stats.data[9] = lp->max_frags_in_a_packet; + stats.data[10] = lp->tx_hw_csums; + stats.data[11] = lp->rx_hw_csums; + + if (copy_to_user(rq->ifr_data, &stats, sizeof(stats))) { + return -EFAULT; + } + ret = 0; + break; + } + default: + return -EOPNOTSUPP; /* All other operations not supported */ + } + return ret; +} + +static int xenet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct net_local *lp = (struct net_local *) netdev_priv(dev); + + /* gmii_ioctl_data has 4 u16 fields: phy_id, reg_num, val_in & val_out */ + struct mii_ioctl_data *data = (struct mii_ioctl_data *) &rq->ifr_data; + struct { + __u16 threshold; + __u32 direction; + } thr_arg; + struct { + __u16 waitbound; + __u32 direction; + } wbnd_arg; + + int ret; + u32 threshold, timer; + XLlDma_BdRing *RingPtr; + u32 *dma_int_mask_ptr; + + switch (cmd) { + case SIOCETHTOOL: + /* DEPRECATED */ + return xenet_do_ethtool_ioctl(dev, rq); + case SIOCGMIIPHY: /* Get address of GMII PHY in use. */ + case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */ + data->phy_id = lp->gmii_addr; + /* Fall Through */ + + case SIOCGMIIREG: /* Read GMII PHY register. */ + case SIOCDEVPRIVATE + 1: /* for binary compat, remove in 2.5 */ + if (data->phy_id > 31 || data->reg_num > 31) + return -ENXIO; + + /* Stop the PHY timer to prevent reentrancy. */ + del_timer_sync(&lp->phy_timer); + + _XLlTemac_PhyRead(&lp->Emac, data->phy_id, data->reg_num, + &data->val_out); + + /* Start the PHY timer up again. */ + lp->phy_timer.expires = jiffies + 2 * HZ; + add_timer(&lp->phy_timer); + return 0; + + case SIOCSMIIREG: /* Write GMII PHY register. */ + case SIOCDEVPRIVATE + 2: /* for binary compat, remove in 2.5 */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (data->phy_id > 31 || data->reg_num > 31) + return -ENXIO; + + /* Stop the PHY timer to prevent reentrancy. */ + del_timer_sync(&lp->phy_timer); + + _XLlTemac_PhyWrite(&lp->Emac, data->phy_id, data->reg_num, + data->val_in); + + /* Start the PHY timer up again. */ + lp->phy_timer.expires = jiffies + 2 * HZ; + add_timer(&lp->phy_timer); + return 0; + + case SIOCDEVPRIVATE + 3: /* set THRESHOLD */ + if (XLlTemac_IsFifo(&lp->Emac)) + return -EFAULT; + + if (copy_from_user(&thr_arg, rq->ifr_data, sizeof(thr_arg))) + return -EFAULT; + + if (thr_arg.direction == XTE_SEND) { + RingPtr = &lp->Dma.TxBdRing; + } else { + RingPtr = &lp->Dma.RxBdRing; + } + XLlDma_BdRingGetCoalesce(RingPtr, &threshold, &timer); + if (thr_arg.direction == XTE_SEND) { + RingPtr = &lp->Dma.TxBdRing; + } else { + RingPtr = &lp->Dma.RxBdRing; + } + if ((ret = XLlDma_BdRingSetCoalesce(RingPtr, thr_arg.threshold, + timer)) != XST_SUCCESS) { + return -EIO; + } + return 0; + + case SIOCDEVPRIVATE + 4: /* set WAITBOUND */ + if (!(XLlTemac_IsDma(&lp->Emac))) + return -EFAULT; + + if (copy_from_user(&wbnd_arg, rq->ifr_data, sizeof(wbnd_arg))) + return -EFAULT; + + if (wbnd_arg.direction == XTE_SEND) { + RingPtr = &lp->Dma.TxBdRing; + } else { + RingPtr = &lp->Dma.RxBdRing; + } + XLlDma_BdRingGetCoalesce(RingPtr, &threshold, &timer); + if (wbnd_arg.direction == XTE_SEND) { + RingPtr = &lp->Dma.TxBdRing; + dma_int_mask_ptr = &dma_tx_int_mask; + } else { + RingPtr = &lp->Dma.RxBdRing; + dma_int_mask_ptr = &dma_rx_int_mask; + } + if (wbnd_arg.waitbound == 0) { + wbnd_arg.waitbound = 1; + *dma_int_mask_ptr = XLLDMA_CR_IRQ_ALL_EN_MASK & ~XLLDMA_CR_IRQ_DELAY_EN_MASK; + } + if ((ret = XLlDma_BdRingSetCoalesce(RingPtr, threshold, + wbnd_arg.waitbound)) != XST_SUCCESS) { + return -EIO; + } + XLlDma_mBdRingIntEnable(RingPtr, *dma_int_mask_ptr); + + return 0; + + case SIOCDEVPRIVATE + 5: /* get THRESHOLD */ + if (!(XLlTemac_IsDma(&lp->Emac))) + return -EFAULT; + + if (copy_from_user(&thr_arg, rq->ifr_data, sizeof(thr_arg))) + return -EFAULT; + + if (thr_arg.direction == XTE_SEND) { + RingPtr = &lp->Dma.TxBdRing; + } else { + RingPtr = &lp->Dma.RxBdRing; + } + XLlDma_BdRingGetCoalesce(RingPtr, + (u32 *) &(thr_arg.threshold), &timer); + if (copy_to_user(rq->ifr_data, &thr_arg, sizeof(thr_arg))) { + return -EFAULT; + } + return 0; + + case SIOCDEVPRIVATE + 6: /* get WAITBOUND */ + if (!(XLlTemac_IsDma(&lp->Emac))) + return -EFAULT; + + if (copy_from_user(&wbnd_arg, rq->ifr_data, sizeof(wbnd_arg))) { + return -EFAULT; + } + if (thr_arg.direction == XTE_SEND) { + RingPtr = &lp->Dma.TxBdRing; + } else { + RingPtr = &lp->Dma.RxBdRing; + } + XLlDma_BdRingGetCoalesce(RingPtr, &threshold, + (u32 *) &(wbnd_arg.waitbound)); + if (copy_to_user(rq->ifr_data, &wbnd_arg, sizeof(wbnd_arg))) { + return -EFAULT; + } + return 0; + + default: + return -EOPNOTSUPP; + } +} + + +/****************************************************************************** + * + * NEW FUNCTIONS FROM LINUX 2.6 + * + ******************************************************************************/ + +static void xtenet_remove_ndev(struct net_device *ndev) +{ + if (ndev) { + struct net_local *lp = netdev_priv(ndev); + + if (XLlTemac_IsDma(&lp->Emac) && (lp->desc_space)) + free_descriptor_skb(ndev); + + iounmap((__force void __iomem *) (lp->Emac.Config.BaseAddress)); + free_netdev(ndev); + } +} + +static int xtenet_remove(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + + unregister_netdev(ndev); + xtenet_remove_ndev(ndev); + + return 0; /* success */ +} + +/* Detect the PHY address by scanning addresses 0 to 31 and + * looking at the MII status register (register 1) and assuming + * the PHY supports 10Mbps full/half duplex. Feel free to change + * this code to match your PHY, or hardcode the address if needed. + */ +/* Use MII register 1 (MII status register) to detect PHY */ +#define PHY_DETECT_REG 1 + +#ifdef CONFIG_XILINX_LLTEMAC_XILINX_1000BASEX +/* Mask used to verify certain PHY features (or register contents) + * in the register above: + * 0x0100: Extended register support + * 0x0180: Unidirectional support + * 0x0040: MF Preamble suppression support + * 0x0008: Auto-negotiation support + */ +#define PHY_DETECT_MASK 0x01C8 +#else +/* Mask used to verify certain PHY features (or register contents) + * in the register above: + * 0x1000: 10Mbps full duplex support + * 0x0800: 10Mbps half duplex support + * 0x0008: Auto-negotiation support + */ +#define PHY_DETECT_MASK 0x1808 +#endif + +static int detect_phy(struct net_local *lp, char *dev_name) +{ + u16 phy_reg; + u32 phy_addr; + + for (phy_addr = 31; phy_addr > 0; phy_addr--) { + _XLlTemac_PhyRead(&lp->Emac, phy_addr, PHY_DETECT_REG, &phy_reg); + + if ((phy_reg != 0xFFFF) && + ((phy_reg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { + /* Found a valid PHY address */ + printk(KERN_INFO "XTemac: PHY detected at address %d.\n", phy_addr); + return phy_addr; + } + } + + printk(KERN_WARNING "XTemac: No PHY detected. Assuming a PHY at address 0\n"); + return 0; /* default to zero */ +} + +static struct net_device_ops xilinx_netdev_ops; + +/* From include/linux/ethtool.h */ +static struct ethtool_ops ethtool_ops = { + .get_settings = xenet_ethtool_get_settings, + .set_settings = xenet_ethtool_set_settings, + .get_drvinfo = xenet_ethtool_get_drvinfo, + .get_regs_len = xenet_ethtool_get_regs_len, + .get_regs = xenet_ethtool_get_regs, + .get_coalesce = xenet_ethtool_get_coalesce, + .set_coalesce = xenet_ethtool_set_coalesce, + .get_ringparam = xenet_ethtool_get_ringparam, + .get_pauseparam = xenet_ethtool_get_pauseparam, +/* .get_rx_csum = xenet_ethtool_get_rx_csum, + .set_rx_csum = xenet_ethtool_set_rx_csum, + .get_tx_csum = xenet_ethtool_get_tx_csum, + .set_tx_csum = xenet_ethtool_set_tx_csum, + .get_sg = xenet_ethtool_get_sg, + .set_sg = xenet_ethtool_set_sg, */ + .get_strings = xenet_ethtool_get_strings, + .get_ethtool_stats = xenet_ethtool_get_ethtool_stats, + .get_sset_count = xenet_ethtool_get_sset_count, +}; + +/** Shared device initialization code */ +static int xtenet_setup( + struct device *dev, + struct resource *r_mem, + struct resource *r_irq, + struct xlltemac_platform_data *pdata) { + int xs; + u32 virt_baddr; /* virtual base address of TEMAC */ + + XLlTemac_Config Temac_Config; + + struct net_device *ndev = NULL; + struct net_local *lp = NULL; + + int rc = 0; + + /* Create an ethernet device instance */ + ndev = alloc_etherdev(sizeof(struct net_local)); + if (!ndev) { + dev_err(dev, "Could not allocate net device.\n"); + rc = -ENOMEM; + goto error; + } + dev_set_drvdata(dev, ndev); + + SET_NETDEV_DEV(ndev, dev); + ndev->irq = r_irq->start; + + /* Initialize the private data used by XEmac_LookupConfig(). + * The private data are zeroed out by alloc_etherdev() already. + */ + lp = netdev_priv(ndev); + lp->ndev = ndev; + lp->dma_irq_r = pdata->ll_dev_dma_rx_irq; + lp->dma_irq_s = pdata->ll_dev_dma_tx_irq; + lp->fifo_irq = pdata->ll_dev_fifo_irq; + + /* Setup the Config structure for the XLlTemac_CfgInitialize() call. */ + Temac_Config.BaseAddress = r_mem->start; +#if 0 + Config.RxPktFifoDepth = pdata->rx_pkt_fifo_depth; + Config.TxPktFifoDepth = pdata->tx_pkt_fifo_depth; + Config.MacFifoDepth = pdata->mac_fifo_depth; + Config.IpIfDmaConfig = pdata->dma_mode; +#endif + Temac_Config.TxCsum = pdata->tx_csum; + Temac_Config.RxCsum = pdata->rx_csum; + Temac_Config.LLDevType = pdata->ll_dev_type; + Temac_Config.LLDevBaseAddress = pdata->ll_dev_baseaddress; + Temac_Config.PhyType = pdata->phy_type; + + /* Get the virtual base address for the device */ + virt_baddr = (__force u32) ioremap(r_mem->start, + r_mem->end - r_mem->start + 1); + if (0 == virt_baddr) { + dev_err(dev, "XLlTemac: Could not allocate iomem.\n"); + rc = -EIO; + goto error; + } + + if (XLlTemac_CfgInitialize(&lp->Emac, &Temac_Config, virt_baddr) != + XST_SUCCESS) { + dev_err(dev, "XLlTemac: Could not initialize device.\n"); + + rc = -ENODEV; + goto error; + } + + /* Set the MAC address from platform data */ + memcpy(ndev->dev_addr, pdata->mac_addr, 6); + + if (_XLlTemac_SetMacAddress(&lp->Emac, ndev->dev_addr) != XST_SUCCESS) { + /* should not fail right after an initialize */ + dev_err(dev, "XLlTemac: could not set MAC address.\n"); + rc = -EIO; + goto error; + } + + dev_info(dev, + "MAC address is now %2x:%2x:%2x:%2x:%2x:%2x\n", + pdata->mac_addr[0], pdata->mac_addr[1], + pdata->mac_addr[2], pdata->mac_addr[3], + pdata->mac_addr[4], pdata->mac_addr[5]); + + if (ndev->mtu > XTE_JUMBO_MTU) + ndev->mtu = XTE_JUMBO_MTU; + + lp->frame_size = ndev->mtu + XTE_HDR_SIZE + XTE_TRL_SIZE; + + if (XLlTemac_IsDma(&lp->Emac)) { + int result; + + dev_err(dev, "XLlTemac: using DMA mode.\n"); + + if (pdata->dcr_host) { + printk("XLlTemac: DCR address: 0x%0x\n", pdata->ll_dev_baseaddress); + XLlDma_Initialize(&lp->Dma, pdata->ll_dev_baseaddress); + lp->virt_dma_addr = pdata->ll_dev_baseaddress; + } else { + virt_baddr = (__force u32) ioremap(pdata->ll_dev_baseaddress, 4096); + lp->virt_dma_addr = virt_baddr; + if (0 == virt_baddr) { + dev_err(dev, + "XLlTemac: Could not allocate iomem for local link connected device.\n"); + rc = -EIO; + goto error; + } + printk("XLlTemac: Dma base address: phy: 0x%x, virt: 0x%x\n", pdata->ll_dev_baseaddress, virt_baddr); + XLlDma_Initialize(&lp->Dma, virt_baddr); + } + + xilinx_netdev_ops.ndo_start_xmit = xenet_DmaSend; + + result = descriptor_init(ndev); + if (result) { + rc = -EIO; + goto error; + } + + /* set the packet threshold and wait bound for both TX/RX directions */ + if (DFT_TX_WAITBOUND == 0) { + dma_tx_int_mask = XLLDMA_CR_IRQ_ALL_EN_MASK & ~XLLDMA_CR_IRQ_DELAY_EN_MASK; + xs = XLlDma_BdRingSetCoalesce(&lp->Dma.TxBdRing, DFT_TX_THRESHOLD, 1); + } else { + xs = XLlDma_BdRingSetCoalesce(&lp->Dma.TxBdRing, DFT_TX_THRESHOLD, DFT_TX_WAITBOUND); + } + if (xs != XST_SUCCESS) { + dev_err(dev, + "XLlTemac: could not set SEND pkt threshold/waitbound, ERROR %d", + xs); + } + XLlDma_mBdRingIntEnable(&lp->Dma.TxBdRing, dma_tx_int_mask); + + if (DFT_RX_WAITBOUND == 0) { + dma_rx_int_mask = XLLDMA_CR_IRQ_ALL_EN_MASK & ~XLLDMA_CR_IRQ_DELAY_EN_MASK; + xs = XLlDma_BdRingSetCoalesce(&lp->Dma.RxBdRing, DFT_RX_THRESHOLD, 1); + } else { + xs = XLlDma_BdRingSetCoalesce(&lp->Dma.RxBdRing, DFT_RX_THRESHOLD, DFT_RX_WAITBOUND); + } + if (xs != XST_SUCCESS) { + dev_err(dev, + "XLlTemac: Could not set RECV pkt threshold/waitbound ERROR %d", + xs); + } + XLlDma_mBdRingIntEnable(&lp->Dma.RxBdRing, dma_rx_int_mask); + } + else { + dev_err(dev, + "XLlTemac: using FIFO direct interrupt driven mode.\n"); + + virt_baddr = (__force u32) ioremap(pdata->ll_dev_baseaddress, + 4096); + if (0 == virt_baddr) { + dev_err(dev, + "XLlTemac: Could not allocate iomem for local link connected device.\n"); + rc = -EIO; + goto error; + } + printk("XLlTemac: Fifo base address: 0x%0x\n", virt_baddr); + XLlFifo_Initialize(&lp->Fifo, virt_baddr); + + xilinx_netdev_ops.ndo_start_xmit = xenet_FifoSend; + } + + /** Scan to find the PHY */ + lp->gmii_addr = detect_phy(lp, ndev->name); + + + /* initialize the netdev structure */ + + ndev->netdev_ops = &xilinx_netdev_ops; + + if (XLlTemac_IsDma(&lp->Emac)) { + ndev->features = NETIF_F_SG | NETIF_F_FRAGLIST; + + if (XLlTemac_IsTxCsum(&lp->Emac) == TRUE) { + /* + * This hardware only supports proper checksum calculations + * on TCP/UDP packets. + */ + ndev->features |= NETIF_F_IP_CSUM; + } + if (XLlTemac_IsRxCsum(&lp->Emac) == TRUE) { + lp->local_features |= LOCAL_FEATURE_RX_CSUM; + } + } + + ndev->watchdog_timeo = TX_TIMEOUT; + + /* init the stats */ + lp->max_frags_in_a_packet = 0; + lp->tx_hw_csums = 0; + lp->rx_hw_csums = 0; + +#if ! XTE_AUTOSTRIPPING + lp->stripping = + (XLlTemac_GetOptions(&(lp->Emac)) & XTE_FCS_STRIP_OPTION) != 0; +#endif + + /* Set ethtool IOCTL handler vectors. + * xenet_do_ethtool_ioctl() is deprecated. + */ + SET_ETHTOOL_OPS(ndev, ðtool_ops); + + rc = register_netdev(ndev); + if (rc) { + dev_err(dev, + "%s: Cannot register net device, aborting.\n", + ndev->name); + goto error; /* rc is already set here... */ + } + + dev_info(dev, + "%s: Xilinx TEMAC at 0x%08X mapped to 0x%08X, irq=%d\n", + ndev->name, + (unsigned int)r_mem->start, + lp->Emac.Config.BaseAddress, + ndev->irq); + + return 0; + +error: + if (ndev) { + xtenet_remove_ndev(ndev); + } + return rc; +} + +#if 0 +static int xtenet_probe(struct device *dev) +{ + struct resource *r_irq = NULL; /* Interrupt resources */ + struct resource *r_mem = NULL; /* IO mem resources */ + struct xlltemac_platform_data *pdata; + struct platform_device *pdev = to_platform_device(dev); + + /* param check */ + if (!pdev) { + dev_err(dev, "Probe called with NULL param.\n"); + return -ENODEV; + } + + pdata = (struct xlltemac_platform_data *) pdev->dev.platform_data; + if (!pdata) { + dev_err(dev, "Couldn't find platform data.\n"); + + return -ENODEV; + } + + /* Get iospace and an irq for the device */ + r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r_irq || !r_mem) { + dev_err(dev, "IO resource(s) not found.\n"); + return -ENODEV; + } + + return xtenet_setup(dev, r_mem, r_irq, pdata); +} +#endif + + +#ifdef CONFIG_OF +static u32 get_u32(struct platform_device *op, const char *s) { + u32 *p = (u32 *)of_get_property(op->dev.of_node, s, NULL); + if(p) { + return *p; + } else { + dev_warn(&op->dev, "Parameter %s not found, defaulting to false.\n", s); + return FALSE; + } +} + +static struct net_device_ops xilinx_netdev_ops = { + .ndo_open = xenet_open, + .ndo_stop = xenet_close, + .ndo_start_xmit = NULL, + .ndo_do_ioctl = xenet_ioctl, + .ndo_change_mtu = xenet_change_mtu, + .ndo_tx_timeout = xenet_tx_timeout, + .ndo_get_stats = xenet_get_stats, + .ndo_set_mac_address = eth_mac_addr, + .ndo_set_rx_mode = xenet_set_multicast_list, + .ndo_validate_addr = eth_validate_addr, +}; + +static struct of_device_id xtenet_fifo_of_match[] = { + { .compatible = "xlnx,xps-ll-fifo-1.00.a", }, + { .compatible = "xlnx,xps-ll-fifo-1.00.b", }, + { .compatible = "xlnx,xps-ll-fifo-1.01.a", }, + { /* end of list */ }, +}; + +static struct of_device_id xtenet_sdma_of_match[] = { + { .compatible = "xlnx,ll-dma-1.00.a", }, + { /* end of list */ }, +}; + +static int xtenet_of_probe(struct platform_device *op) +{ + struct resource r_irq_struct; + struct resource r_mem_struct; + struct resource r_connected_mem_struct; + struct resource r_connected_irq_struct; + struct xlltemac_platform_data pdata_struct; + + struct resource *r_irq = &r_irq_struct; /* Interrupt resources */ + struct resource *r_mem = &r_mem_struct; /* IO mem resources */ + struct xlltemac_platform_data *pdata = &pdata_struct; + const void *mac_address; + int rc = 0; + const phandle *llink_connected_handle; + struct device_node *llink_connected_node; + u32 *dcrreg_property; + + /* + * Make sure the locks are initialized + */ + spin_lock_init(&XTE_spinlock); + spin_lock_init(&XTE_tx_spinlock); + spin_lock_init(&XTE_rx_spinlock); + + INIT_LIST_HEAD(&sentQueue); + INIT_LIST_HEAD(&receivedQueue); + + spin_lock_init(&sentQueueSpin); + spin_lock_init(&receivedQueueSpin); + + printk(KERN_INFO "Device Tree Probing \'%s\'\n", + op->dev.of_node->name); + + /* Get iospace for the device */ + rc = of_address_to_resource(op->dev.of_node, 0, r_mem); + if(rc) { + dev_warn(&op->dev, "invalid address\n"); + return rc; + } + + /* Get IRQ for the device */ + rc = of_irq_to_resource(op->dev.of_node, 0, r_irq); + if(!rc) { + dev_warn(&op->dev, "no IRQ found.\n"); + return rc; + } + + pdata_struct.tx_csum = get_u32(op, "xlnx,txcsum"); + pdata_struct.rx_csum = get_u32(op, "xlnx,rxcsum"); + pdata_struct.phy_type = get_u32(op, "xlnx,phy-type"); + llink_connected_handle = + of_get_property(op->dev.of_node, "llink-connected", NULL); + if(!llink_connected_handle) { + dev_warn(&op->dev, "no Locallink connection found.\n"); + return rc; + } + + llink_connected_node = + of_find_node_by_phandle(*llink_connected_handle); + rc = of_address_to_resource( + llink_connected_node, + 0, + &r_connected_mem_struct); + + /** Get the right information from whatever the locallink is + connected to. */ + if(of_match_node(xtenet_fifo_of_match, llink_connected_node)) { + /** Connected to a fifo. */ + + if(rc) { + dev_warn(&op->dev, "invalid address\n"); + return rc; + } + + pdata_struct.ll_dev_baseaddress = r_connected_mem_struct.start; + pdata_struct.ll_dev_type = XPAR_LL_FIFO; + pdata_struct.ll_dev_dma_rx_irq = 0; + pdata_struct.ll_dev_dma_tx_irq = 0; + + rc = of_irq_to_resource( + llink_connected_node, + 0, + &r_connected_irq_struct); + if(!rc) { + dev_warn(&op->dev, "no IRQ found.\n"); + return rc; + } + pdata_struct.ll_dev_fifo_irq = r_connected_irq_struct.start; + pdata_struct.dcr_host = 0x0; + } else if(of_match_node(xtenet_sdma_of_match, llink_connected_node)) { + /** Connected to a dma port, default to 405 type dma */ + pdata->dcr_host = 0; + if(rc) { + /* no address was found, might be 440, check for dcr reg */ + + dcrreg_property = (u32 *)of_get_property(llink_connected_node, "dcr-reg", NULL); + if(dcrreg_property) { + r_connected_mem_struct.start = *dcrreg_property; + pdata->dcr_host = 0xFF; + } else { + dev_warn(&op->dev, "invalid address\n"); + return rc; + } + } + + pdata_struct.ll_dev_baseaddress = r_connected_mem_struct.start; + pdata_struct.ll_dev_type = XPAR_LL_DMA; + + rc = of_irq_to_resource( + llink_connected_node, + 0, + &r_connected_irq_struct); + if(!rc) { + dev_warn(&op->dev, "First IRQ not found.\n"); + return rc; + } + pdata_struct.ll_dev_dma_rx_irq = r_connected_irq_struct.start; + rc = of_irq_to_resource( + llink_connected_node, + 1, + &r_connected_irq_struct); + if(!rc) { + dev_warn(&op->dev, "Second IRQ not found.\n"); + return rc; + } + pdata_struct.ll_dev_dma_tx_irq = r_connected_irq_struct.start; + + pdata_struct.ll_dev_fifo_irq = 0; + } else { + dev_warn(&op->dev, "Locallink connection not matched.\n"); + return rc; + } + + of_node_put(llink_connected_node); + mac_address = of_get_mac_address(op->dev.of_node); + if(mac_address) { + memcpy(pdata_struct.mac_addr, mac_address, 6); + } else { + dev_warn(&op->dev, "No MAC address found.\n"); + } + + return xtenet_setup(&op->dev, r_mem, r_irq, pdata); +} + +static int xtenet_of_remove(struct platform_device *op) +{ + return xtenet_remove(&op->dev); +} + +static struct of_device_id xtenet_of_match[] = { + { .compatible = "xlnx,xps-ll-temac-1.00.a", }, + { .compatible = "xlnx,xps-ll-temac-1.00.b", }, + { .compatible = "xlnx,xps-ll-temac-1.01.a", }, + { .compatible = "xlnx,xps-ll-temac-1.01.b", }, + { /* end of list */ }, +}; + +MODULE_DEVICE_TABLE(of, xtenet_of_match); +#endif + +static struct platform_driver xtenet_of_driver = { + .probe = xtenet_of_probe, + .remove = xtenet_of_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(xtenet_of_match), + }, +}; + +module_platform_driver(xtenet_of_driver); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION(DRIVER_DESCRIPTION); +MODULE_LICENSE("GPL");