summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/xilinx
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-03-20 21:04:47 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-03-20 21:04:47 -0700
commit3b59bf081622b6446db77ad06c93fe23677bc533 (patch)
tree3f4bb5a27c90cc86994a1f6d3c53fbf9208003cb /drivers/net/ethernet/xilinx
parente45836fafe157df137a837093037f741ad8f4c90 (diff)
parentbbdb32cb5b73597386913d052165423b9d736145 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking merge from David Miller: "1) Move ixgbe driver over to purely page based buffering on receive. From Alexander Duyck. 2) Add receive packet steering support to e1000e, from Bruce Allan. 3) Convert TCP MD5 support over to RCU, from Eric Dumazet. 4) Reduce cpu usage in handling out-of-order TCP packets on modern systems, also from Eric Dumazet. 5) Support the IP{,V6}_UNICAST_IF socket options, making the wine folks happy, from Erich Hoover. 6) Support VLAN trunking from guests in hyperv driver, from Haiyang Zhang. 7) Support byte-queue-limtis in r8169, from Igor Maravic. 8) Outline code intended for IP_RECVTOS in IP_PKTOPTIONS existed but was never properly implemented, Jiri Benc fixed that. 9) 64-bit statistics support in r8169 and 8139too, from Junchang Wang. 10) Support kernel side dump filtering by ctmark in netfilter ctnetlink, from Pablo Neira Ayuso. 11) Support byte-queue-limits in gianfar driver, from Paul Gortmaker. 12) Add new peek socket options to assist with socket migration, from Pavel Emelyanov. 13) Add sch_plug packet scheduler whose queue is controlled by userland daemons using explicit freeze and release commands. From Shriram Rajagopalan. 14) Fix FCOE checksum offload handling on transmit, from Yi Zou." * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1846 commits) Fix pppol2tp getsockname() Remove printk from rds_sendmsg ipv6: fix incorrent ipv6 ipsec packet fragment cpsw: Hook up default ndo_change_mtu. net: qmi_wwan: fix build error due to cdc-wdm dependecy netdev: driver: ethernet: Add TI CPSW driver netdev: driver: ethernet: add cpsw address lookup engine support phy: add am79c874 PHY support mlx4_core: fix race on comm channel bonding: send igmp report for its master fs_enet: Add MPC5125 FEC support and PHY interface selection net: bpf_jit: fix BPF_S_LDX_B_MSH compilation net: update the usage of CHECKSUM_UNNECESSARY fcoe: use CHECKSUM_UNNECESSARY instead of CHECKSUM_PARTIAL on tx net: do not do gso for CHECKSUM_UNNECESSARY in netif_needs_gso ixgbe: Fix issues with SR-IOV loopback when flow control is disabled net/hyperv: Fix the code handling tx busy ixgbe: fix namespace issues when FCoE/DCB is not enabled rtlwifi: Remove unused ETH_ADDR_LEN defines igbvf: Use ETH_ALEN ... Fix up fairly trivial conflicts in drivers/isdn/gigaset/interface.c and drivers/net/usb/{Kconfig,qmi_wwan.c} as per David.
Diffstat (limited to 'drivers/net/ethernet/xilinx')
-rw-r--r--drivers/net/ethernet/xilinx/Kconfig8
-rw-r--r--drivers/net/ethernet/xilinx/Makefile2
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac_main.c9
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet.h508
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet_main.c1669
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c238
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_emaclite.c6
7 files changed, 2432 insertions, 8 deletions
diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig
index d5a826063a82..5778a4ae1164 100644
--- a/drivers/net/ethernet/xilinx/Kconfig
+++ b/drivers/net/ethernet/xilinx/Kconfig
@@ -25,6 +25,14 @@ config XILINX_EMACLITE
---help---
This driver supports the 10/100 Ethernet Lite from Xilinx.
+config XILINX_AXI_EMAC
+ tristate "Xilinx 10/100/1000 AXI Ethernet support"
+ depends on (PPC32 || MICROBLAZE)
+ 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)
diff --git a/drivers/net/ethernet/xilinx/Makefile b/drivers/net/ethernet/xilinx/Makefile
index 5feac734ea45..214205e975e3 100644
--- a/drivers/net/ethernet/xilinx/Makefile
+++ b/drivers/net/ethernet/xilinx/Makefile
@@ -5,3 +5,5 @@
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
+xilinx_emac-objs := xilinx_axienet_main.o xilinx_axienet_mdio.o
+obj-$(CONFIG_XILINX_AXI_EMAC) += xilinx_emac.o
diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c
index f21addb1db95..d21591a2c593 100644
--- a/drivers/net/ethernet/xilinx/ll_temac_main.c
+++ b/drivers/net/ethernet/xilinx/ll_temac_main.c
@@ -327,7 +327,9 @@ static int temac_set_mac_address(struct net_device *ndev, void *address)
memcpy(ndev->dev_addr, address, ETH_ALEN);
if (!is_valid_ether_addr(ndev->dev_addr))
- random_ether_addr(ndev->dev_addr);
+ eth_hw_addr_random(ndev);
+ else
+ ndev->addr_assign_type &= ~NET_ADDR_RANDOM;
/* set up unicast MAC address filter set its mac address */
mutex_lock(&lp->indirect_mutex);
@@ -1011,10 +1013,9 @@ static int __devinit temac_of_probe(struct platform_device *op)
/* Init network device structure */
ndev = alloc_etherdev(sizeof(*lp));
- if (!ndev) {
- dev_err(&op->dev, "could not allocate device.\n");
+ if (!ndev)
return -ENOMEM;
- }
+
ether_setup(ndev);
dev_set_drvdata(&op->dev, ndev);
SET_NETDEV_DEV(ndev, &op->dev);
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
new file mode 100644
index 000000000000..cc83af083fd7
--- /dev/null
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -0,0 +1,508 @@
+/*
+ * Definitions for Xilinx Axi Ethernet device driver.
+ *
+ * Copyright (c) 2009 Secret Lab Technologies, Ltd.
+ * Copyright (c) 2010 Xilinx, Inc. All rights reserved.
+ * Copyright (c) 2012 Daniel Borkmann, <daniel.borkmann@tik.ee.ethz.ch>
+ * Copyright (c) 2012 Ariane Keller, <ariane.keller@tik.ee.ethz.ch>
+ */
+
+#ifndef XILINX_AXIENET_H
+#define XILINX_AXIENET_H
+
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+
+/* 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_JUMBO_FRAME_SIZE (XAE_JUMBO_MTU + XAE_HDR_SIZE + XAE_TRL_SIZE)
+
+/* Configuration options */
+
+/* Accept all incoming packets. Default: disabled (cleared) */
+#define XAE_OPTION_PROMISC (1 << 0)
+
+/* Jumbo frame support for Tx & Rx. Default: disabled (cleared) */
+#define XAE_OPTION_JUMBO (1 << 1)
+
+/* VLAN Rx & Tx frame support. Default: disabled (cleared) */
+#define XAE_OPTION_VLAN (1 << 2)
+
+/* Enable recognition of flow control frames on Rx. Default: enabled (set) */
+#define XAE_OPTION_FLOW_CONTROL (1 << 4)
+
+/* Strip FCS and PAD from incoming frames. Note: PAD from VLAN frames is not
+ * stripped. Default: disabled (set) */
+#define XAE_OPTION_FCS_STRIP (1 << 5)
+
+/* Generate FCS field and add PAD automatically for outgoing frames.
+ * Default: enabled (set) */
+#define XAE_OPTION_FCS_INSERT (1 << 6)
+
+/* Enable Length/Type error checking for incoming frames. When this option is
+ * set, the MAC will filter frames that have a mismatched type/length field
+ * and if XAE_OPTION_REPORT_RXERR is set, the user is notified when these
+ * types of frames are encountered. When this option is cleared, the MAC will
+ * allow these types of frames to be received. Default: enabled (set) */
+#define XAE_OPTION_LENTYPE_ERR (1 << 7)
+
+/* Enable the transmitter. Default: enabled (set) */
+#define XAE_OPTION_TXEN (1 << 11)
+
+/* Enable the receiver. Default: enabled (set) */
+#define XAE_OPTION_RXEN (1 << 12)
+
+/* Default options set when device is initialized or reset */
+#define XAE_OPTION_DEFAULTS \
+ (XAE_OPTION_TXEN | \
+ XAE_OPTION_FLOW_CONTROL | \
+ XAE_OPTION_RXEN)
+
+/* Axi DMA Register definitions */
+
+#define XAXIDMA_TX_CR_OFFSET 0x00000000 /* Channel control */
+#define XAXIDMA_TX_SR_OFFSET 0x00000004 /* Status */
+#define XAXIDMA_TX_CDESC_OFFSET 0x00000008 /* Current descriptor pointer */
+#define XAXIDMA_TX_TDESC_OFFSET 0x00000010 /* Tail descriptor pointer */
+
+#define XAXIDMA_RX_CR_OFFSET 0x00000030 /* Channel control */
+#define XAXIDMA_RX_SR_OFFSET 0x00000034 /* Status */
+#define XAXIDMA_RX_CDESC_OFFSET 0x00000038 /* Current descriptor pointer */
+#define XAXIDMA_RX_TDESC_OFFSET 0x00000040 /* Tail descriptor pointer */
+
+#define XAXIDMA_CR_RUNSTOP_MASK 0x00000001 /* Start/stop DMA channel */
+#define XAXIDMA_CR_RESET_MASK 0x00000004 /* Reset DMA engine */
+
+#define XAXIDMA_BD_NDESC_OFFSET 0x00 /* Next descriptor pointer */
+#define XAXIDMA_BD_BUFA_OFFSET 0x08 /* Buffer address */
+#define XAXIDMA_BD_CTRL_LEN_OFFSET 0x18 /* Control/buffer length */
+#define XAXIDMA_BD_STS_OFFSET 0x1C /* Status */
+#define XAXIDMA_BD_USR0_OFFSET 0x20 /* User IP specific word0 */
+#define XAXIDMA_BD_USR1_OFFSET 0x24 /* User IP specific word1 */
+#define XAXIDMA_BD_USR2_OFFSET 0x28 /* User IP specific word2 */
+#define XAXIDMA_BD_USR3_OFFSET 0x2C /* User IP specific word3 */
+#define XAXIDMA_BD_USR4_OFFSET 0x30 /* User IP specific word4 */
+#define XAXIDMA_BD_ID_OFFSET 0x34 /* Sw ID */
+#define XAXIDMA_BD_HAS_STSCNTRL_OFFSET 0x38 /* Whether has stscntrl strm */
+#define XAXIDMA_BD_HAS_DRE_OFFSET 0x3C /* Whether has DRE */
+
+#define XAXIDMA_BD_HAS_DRE_SHIFT 8 /* Whether has DRE shift */
+#define XAXIDMA_BD_HAS_DRE_MASK 0xF00 /* Whether has DRE mask */
+#define XAXIDMA_BD_WORDLEN_MASK 0xFF /* Whether has DRE mask */
+
+#define XAXIDMA_BD_CTRL_LENGTH_MASK 0x007FFFFF /* Requested len */
+#define XAXIDMA_BD_CTRL_TXSOF_MASK 0x08000000 /* First tx packet */
+#define XAXIDMA_BD_CTRL_TXEOF_MASK 0x04000000 /* Last tx packet */
+#define XAXIDMA_BD_CTRL_ALL_MASK 0x0C000000 /* All control bits */
+
+#define XAXIDMA_DELAY_MASK 0xFF000000 /* Delay timeout counter */
+#define XAXIDMA_COALESCE_MASK 0x00FF0000 /* Coalesce counter */
+
+#define XAXIDMA_DELAY_SHIFT 24
+#define XAXIDMA_COALESCE_SHIFT 16
+
+#define XAXIDMA_IRQ_IOC_MASK 0x00001000 /* Completion intr */
+#define XAXIDMA_IRQ_DELAY_MASK 0x00002000 /* Delay interrupt */
+#define XAXIDMA_IRQ_ERROR_MASK 0x00004000 /* Error interrupt */
+#define XAXIDMA_IRQ_ALL_MASK 0x00007000 /* All interrupts */
+
+/* Default TX/RX Threshold and waitbound values for SGDMA mode */
+#define XAXIDMA_DFT_TX_THRESHOLD 24
+#define XAXIDMA_DFT_TX_WAITBOUND 254
+#define XAXIDMA_DFT_RX_THRESHOLD 24
+#define XAXIDMA_DFT_RX_WAITBOUND 254
+
+#define XAXIDMA_BD_CTRL_TXSOF_MASK 0x08000000 /* First tx packet */
+#define XAXIDMA_BD_CTRL_TXEOF_MASK 0x04000000 /* Last tx packet */
+#define XAXIDMA_BD_CTRL_ALL_MASK 0x0C000000 /* All control bits */
+
+#define XAXIDMA_BD_STS_ACTUAL_LEN_MASK 0x007FFFFF /* Actual len */
+#define XAXIDMA_BD_STS_COMPLETE_MASK 0x80000000 /* Completed */
+#define XAXIDMA_BD_STS_DEC_ERR_MASK 0x40000000 /* Decode error */
+#define XAXIDMA_BD_STS_SLV_ERR_MASK 0x20000000 /* Slave error */
+#define XAXIDMA_BD_STS_INT_ERR_MASK 0x10000000 /* Internal err */
+#define XAXIDMA_BD_STS_ALL_ERR_MASK 0x70000000 /* All errors */
+#define XAXIDMA_BD_STS_RXSOF_MASK 0x08000000 /* First rx pkt */
+#define XAXIDMA_BD_STS_RXEOF_MASK 0x04000000 /* Last rx pkt */
+#define XAXIDMA_BD_STS_ALL_MASK 0xFC000000 /* All status bits */
+
+#define XAXIDMA_BD_MINIMUM_ALIGNMENT 0x40
+
+/* Axi Ethernet registers definition */
+#define XAE_RAF_OFFSET 0x00000000 /* Reset and Address filter */
+#define XAE_TPF_OFFSET 0x00000004 /* Tx Pause Frame */
+#define XAE_IFGP_OFFSET 0x00000008 /* Tx Inter-frame gap adjustment*/
+#define XAE_IS_OFFSET 0x0000000C /* Interrupt status */
+#define XAE_IP_OFFSET 0x00000010 /* Interrupt pending */
+#define XAE_IE_OFFSET 0x00000014 /* Interrupt enable */
+#define XAE_TTAG_OFFSET 0x00000018 /* Tx VLAN TAG */
+#define XAE_RTAG_OFFSET 0x0000001C /* Rx VLAN TAG */
+#define XAE_UAWL_OFFSET 0x00000020 /* Unicast address word lower */
+#define XAE_UAWU_OFFSET 0x00000024 /* Unicast address word upper */
+#define XAE_TPID0_OFFSET 0x00000028 /* VLAN TPID0 register */
+#define XAE_TPID1_OFFSET 0x0000002C /* VLAN TPID1 register */
+#define XAE_PPST_OFFSET 0x00000030 /* PCS PMA Soft Temac Status Reg */
+#define XAE_RCW0_OFFSET 0x00000400 /* Rx Configuration Word 0 */
+#define XAE_RCW1_OFFSET 0x00000404 /* Rx Configuration Word 1 */
+#define XAE_TC_OFFSET 0x00000408 /* Tx Configuration */
+#define XAE_FCC_OFFSET 0x0000040C /* Flow Control Configuration */
+#define XAE_EMMC_OFFSET 0x00000410 /* EMAC mode configuration */
+#define XAE_PHYC_OFFSET 0x00000414 /* RGMII/SGMII configuration */
+#define XAE_MDIO_MC_OFFSET 0x00000500 /* MII Management Config */
+#define XAE_MDIO_MCR_OFFSET 0x00000504 /* MII Management Control */
+#define XAE_MDIO_MWD_OFFSET 0x00000508 /* MII Management Write Data */
+#define XAE_MDIO_MRD_OFFSET 0x0000050C /* MII Management Read Data */
+#define XAE_MDIO_MIS_OFFSET 0x00000600 /* MII Management Interrupt Status */
+#define XAE_MDIO_MIP_OFFSET 0x00000620 /* MII Mgmt Interrupt Pending
+ * register offset */
+#define XAE_MDIO_MIE_OFFSET 0x00000640 /* MII Management Interrupt Enable
+ * register offset */
+#define XAE_MDIO_MIC_OFFSET 0x00000660 /* MII Management Interrupt Clear
+ * register offset. */
+#define XAE_UAW0_OFFSET 0x00000700 /* Unicast address word 0 */
+#define XAE_UAW1_OFFSET 0x00000704 /* Unicast address word 1 */
+#define XAE_FMI_OFFSET 0x00000708 /* Filter Mask Index */
+#define XAE_AF0_OFFSET 0x00000710 /* Address Filter 0 */
+#define XAE_AF1_OFFSET 0x00000714 /* Address Filter 1 */
+
+#define XAE_TX_VLAN_DATA_OFFSET 0x00004000 /* TX VLAN data table address */
+#define XAE_RX_VLAN_DATA_OFFSET 0x00008000 /* RX VLAN data table address */
+#define XAE_MCAST_TABLE_OFFSET 0x00020000 /* Multicast table address */
+
+/* Bit Masks for Axi Ethernet RAF register */
+#define XAE_RAF_MCSTREJ_MASK 0x00000002 /* Reject receive multicast
+ * destination address */
+#define XAE_RAF_BCSTREJ_MASK 0x00000004 /* Reject receive broadcast
+ * destination address */
+#define XAE_RAF_TXVTAGMODE_MASK 0x00000018 /* Tx VLAN TAG mode */
+#define XAE_RAF_RXVTAGMODE_MASK 0x00000060 /* Rx VLAN TAG mode */
+#define XAE_RAF_TXVSTRPMODE_MASK 0x00000180 /* Tx VLAN STRIP mode */
+#define XAE_RAF_RXVSTRPMODE_MASK 0x00000600 /* Rx VLAN STRIP mode */
+#define XAE_RAF_NEWFNCENBL_MASK 0x00000800 /* New function mode */
+#define XAE_RAF_EMULTIFLTRENBL_MASK 0x00001000 /* Exteneded Multicast
+ * Filtering mode
+ */
+#define XAE_RAF_STATSRST_MASK 0x00002000 /* Stats. Counter Reset */
+#define XAE_RAF_RXBADFRMEN_MASK 0x00004000 /* Recv Bad Frame Enable */
+#define XAE_RAF_TXVTAGMODE_SHIFT 3 /* Tx Tag mode shift bits */
+#define XAE_RAF_RXVTAGMODE_SHIFT 5 /* Rx Tag mode shift bits */
+#define XAE_RAF_TXVSTRPMODE_SHIFT 7 /* Tx strip mode shift bits*/
+#define XAE_RAF_RXVSTRPMODE_SHIFT 9 /* Rx Strip mode shift bits*/
+
+/* Bit Masks for Axi Ethernet TPF and IFGP registers */
+#define XAE_TPF_TPFV_MASK 0x0000FFFF /* Tx pause frame value */
+#define XAE_IFGP0_IFGP_MASK 0x0000007F /* Transmit inter-frame
+ * gap adjustment value */
+
+/* Bit Masks for Axi Ethernet IS, IE and IP registers, Same masks apply
+ * for all 3 registers. */
+#define XAE_INT_HARDACSCMPLT_MASK 0x00000001 /* Hard register access
+ * complete */
+#define XAE_INT_AUTONEG_MASK 0x00000002 /* Auto negotiation
+ * complete */
+#define XAE_INT_RXCMPIT_MASK 0x00000004 /* Rx complete */
+#define XAE_INT_RXRJECT_MASK 0x00000008 /* Rx frame rejected */
+#define XAE_INT_RXFIFOOVR_MASK 0x00000010 /* Rx fifo overrun */
+#define XAE_INT_TXCMPIT_MASK 0x00000020 /* Tx complete */
+#define XAE_INT_RXDCMLOCK_MASK 0x00000040 /* Rx Dcm Lock */
+#define XAE_INT_MGTRDY_MASK 0x00000080 /* MGT clock Lock */
+#define XAE_INT_PHYRSTCMPLT_MASK 0x00000100 /* Phy Reset complete */
+#define XAE_INT_ALL_MASK 0x0000003F /* All the ints */
+
+#define XAE_INT_RECV_ERROR_MASK \
+ (XAE_INT_RXRJECT_MASK | XAE_INT_RXFIFOOVR_MASK) /* INT bits that
+ * indicate receive
+ * errors */
+
+/* Bit masks for Axi Ethernet VLAN TPID Word 0 register */
+#define XAE_TPID_0_MASK 0x0000FFFF /* TPID 0 */
+#define XAE_TPID_1_MASK 0xFFFF0000 /* TPID 1 */
+
+/* Bit masks for Axi Ethernet VLAN TPID Word 1 register */
+#define XAE_TPID_2_MASK 0x0000FFFF /* TPID 0 */
+#define XAE_TPID_3_MASK 0xFFFF0000 /* TPID 1 */
+
+/* Bit masks for Axi Ethernet RCW1 register */
+#define XAE_RCW1_RST_MASK 0x80000000 /* Reset */
+#define XAE_RCW1_JUM_MASK 0x40000000 /* Jumbo frame enable */
+#define XAE_RCW1_FCS_MASK 0x20000000 /* In-Band FCS enable
+ * (FCS not stripped) */
+#define XAE_RCW1_RX_MASK 0x10000000 /* Receiver enable */
+#define XAE_RCW1_VLAN_MASK 0x08000000 /* VLAN frame enable */
+#define XAE_RCW1_LT_DIS_MASK 0x02000000 /* Length/type field valid check
+ * disable */
+#define XAE_RCW1_CL_DIS_MASK 0x01000000 /* Control frame Length check
+ * disable */
+#define XAE_RCW1_PAUSEADDR_MASK 0x0000FFFF /* Pause frame source address
+ * bits [47:32]. Bits [31:0] are
+ * stored in register RCW0 */
+
+/* Bit masks for Axi Ethernet TC register */
+#define XAE_TC_RST_MASK 0x80000000 /* Reset */
+#define XAE_TC_JUM_MASK 0x40000000 /* Jumbo frame enable */
+#define XAE_TC_FCS_MASK 0x20000000 /* In-Band FCS enable
+ * (FCS not generated) */
+#define XAE_TC_TX_MASK 0x10000000 /* Transmitter enable */
+#define XAE_TC_VLAN_MASK 0x08000000 /* VLAN frame enable */
+#define XAE_TC_IFG_MASK 0x02000000 /* Inter-frame gap adjustment
+ * enable */
+
+/* Bit masks for Axi Ethernet FCC register */
+#define XAE_FCC_FCRX_MASK 0x20000000 /* Rx flow control enable */
+#define XAE_FCC_FCTX_MASK 0x40000000 /* Tx flow control enable */
+
+/* Bit masks for Axi Ethernet EMMC register */
+#define XAE_EMMC_LINKSPEED_MASK 0xC0000000 /* Link speed */
+#define XAE_EMMC_RGMII_MASK 0x20000000 /* RGMII mode enable */
+#define XAE_EMMC_SGMII_MASK 0x10000000 /* SGMII mode enable */
+#define XAE_EMMC_GPCS_MASK 0x08000000 /* 1000BaseX mode enable */
+#define XAE_EMMC_HOST_MASK 0x04000000 /* Host interface enable */
+#define XAE_EMMC_TX16BIT 0x02000000 /* 16 bit Tx client enable */
+#define XAE_EMMC_RX16BIT 0x01000000 /* 16 bit Rx client enable */
+#define XAE_EMMC_LINKSPD_10 0x00000000 /* Link Speed mask for 10 Mbit */
+#define XAE_EMMC_LINKSPD_100 0x40000000 /* Link Speed mask for 100 Mbit */
+#define XAE_EMMC_LINKSPD_1000 0x80000000 /* Link Speed mask for 1000 Mbit */
+
+/* Bit masks for Axi Ethernet PHYC register */
+#define XAE_PHYC_SGMIILINKSPEED_MASK 0xC0000000 /* SGMII link speed mask*/
+#define XAE_PHYC_RGMIILINKSPEED_MASK 0x0000000C /* RGMII link speed */
+#define XAE_PHYC_RGMIIHD_MASK 0x00000002 /* RGMII Half-duplex */
+#define XAE_PHYC_RGMIILINK_MASK 0x00000001 /* RGMII link status */
+#define XAE_PHYC_RGLINKSPD_10 0x00000000 /* RGMII link 10 Mbit */
+#define XAE_PHYC_RGLINKSPD_100 0x00000004 /* RGMII link 100 Mbit */
+#define XAE_PHYC_RGLINKSPD_1000 0x00000008 /* RGMII link 1000 Mbit */
+#define XAE_PHYC_SGLINKSPD_10 0x00000000 /* SGMII link 10 Mbit */
+#define XAE_PHYC_SGLINKSPD_100 0x40000000 /* SGMII link 100 Mbit */
+#define XAE_PHYC_SGLINKSPD_1000 0x80000000 /* SGMII link 1000 Mbit */
+
+/* Bit masks for Axi Ethernet MDIO interface MC register */
+#define XAE_MDIO_MC_MDIOEN_MASK 0x00000040 /* MII management enable */
+#define XAE_MDIO_MC_CLOCK_DIVIDE_MAX 0x3F /* Maximum MDIO divisor */
+
+/* Bit masks for Axi Ethernet MDIO interface MCR register */
+#define XAE_MDIO_MCR_PHYAD_MASK 0x1F000000 /* Phy Address Mask */
+#define XAE_MDIO_MCR_PHYAD_SHIFT 24 /* Phy Address Shift */
+#define XAE_MDIO_MCR_REGAD_MASK 0x001F0000 /* Reg Address Mask */
+#define XAE_MDIO_MCR_REGAD_SHIFT 16 /* Reg Address Shift */
+#define XAE_MDIO_MCR_OP_MASK 0x0000C000 /* Operation Code Mask */
+#define XAE_MDIO_MCR_OP_SHIFT 13 /* Operation Code Shift */
+#define XAE_MDIO_MCR_OP_READ_MASK 0x00008000 /* Op Code Read Mask */
+#define XAE_MDIO_MCR_OP_WRITE_MASK 0x00004000 /* Op Code Write Mask */
+#define XAE_MDIO_MCR_INITIATE_MASK 0x00000800 /* Ready Mask */
+#define XAE_MDIO_MCR_READY_MASK 0x00000080 /* Ready Mask */
+
+/* Bit masks for Axi Ethernet MDIO interface MIS, MIP, MIE, MIC registers */
+#define XAE_MDIO_INT_MIIM_RDY_MASK 0x00000001 /* MIIM Interrupt */
+
+/* Bit masks for Axi Ethernet UAW1 register */
+#define XAE_UAW1_UNICASTADDR_MASK 0x0000FFFF /* Station address bits
+ * [47:32]; Station address
+ * bits [31:0] are stored in
+ * register UAW0 */
+
+/* Bit masks for Axi Ethernet FMI register */
+#define XAE_FMI_PM_MASK 0x80000000 /* Promis. mode enable */
+#define XAE_FMI_IND_MASK 0x00000003 /* Index Mask */
+
+#define XAE_MDIO_DIV_DFT 29 /* Default MDIO clock divisor */
+
+/* Defines for different options for C_PHY_TYPE parameter in Axi Ethernet IP */
+#define XAE_PHY_TYPE_MII 0
+#define XAE_PHY_TYPE_GMII 1
+#define XAE_PHY_TYPE_RGMII_1_3 2
+#define XAE_PHY_TYPE_RGMII_2_0 3
+#define XAE_PHY_TYPE_SGMII 4
+#define XAE_PHY_TYPE_1000BASE_X 5
+
+#define XAE_MULTICAST_CAM_TABLE_NUM 4 /* Total number of entries in the
+ * hardware multicast table. */
+
+/* Axi Ethernet Synthesis features */
+#define XAE_FEATURE_PARTIAL_RX_CSUM (1 << 0)
+#define XAE_FEATURE_PARTIAL_TX_CSUM (1 << 1)
+#define XAE_FEATURE_FULL_RX_CSUM (1 << 2)
+#define XAE_FEATURE_FULL_TX_CSUM (1 << 3)
+
+#define XAE_NO_CSUM_OFFLOAD 0
+
+#define XAE_FULL_CSUM_STATUS_MASK 0x00000038
+#define XAE_IP_UDP_CSUM_VALIDATED 0x00000003
+#define XAE_IP_TCP_CSUM_VALIDATED 0x00000002
+
+#define DELAY_OF_ONE_MILLISEC 1000
+
+/**
+ * struct axidma_bd - Axi Dma buffer descriptor layout
+ * @next: MM2S/S2MM Next Descriptor Pointer
+ * @reserved1: Reserved and not used
+ * @phys: MM2S/S2MM Buffer Address
+ * @reserved2: Reserved and not used
+ * @reserved3: Reserved and not used
+ * @reserved4: Reserved and not used
+ * @cntrl: MM2S/S2MM Control value
+ * @status: MM2S/S2MM Status value
+ * @app0: MM2S/S2MM User Application Field 0.
+ * @app1: MM2S/S2MM User Application Field 1.
+ * @app2: MM2S/S2MM User Application Field 2.
+ * @app3: MM2S/S2MM User Application Field 3.
+ * @app4: MM2S/S2MM User Application Field 4.
+ * @sw_id_offset: MM2S/S2MM Sw ID
+ * @reserved5: Reserved and not used
+ * @reserved6: Reserved and not used
+ */
+struct axidma_bd {
+ u32 next; /* Physical address of next buffer descriptor */
+ u32 reserved1;
+ u32 phys;
+ u32 reserved2;
+ u32 reserved3;
+ u32 reserved4;
+ u32 cntrl;
+ u32 status;
+ u32 app0;
+ u32 app1; /* TX start << 16 | insert */
+ u32 app2; /* TX csum seed */
+ u32 app3;
+ u32 app4;
+ u32 sw_id_offset;
+ u32 reserved5;
+ u32 reserved6;
+};
+
+/**
+ * struct axienet_local - axienet private per device data
+ * @ndev: Pointer for net_device to which it will be attached.
+ * @dev: Pointer to device structure
+ * @phy_dev: Pointer to PHY device structure attached to the axienet_local
+ * @phy_node: Pointer to device node structure
+ * @mii_bus: Pointer to MII bus structure
+ * @mdio_irqs: IRQs table for MDIO bus required in mii_bus structure
+ * @regs: Base address for the axienet_local device address space
+ * @dma_regs: Base address for the axidma device address space
+ * @dma_err_tasklet: Tasklet structure to process Axi DMA errors
+ * @tx_irq: Axidma TX IRQ number
+ * @rx_irq: Axidma RX IRQ number
+ * @temac_type: axienet type to identify between soft and hard temac
+ * @phy_type: Phy type to identify between MII/GMII/RGMII/SGMII/1000 Base-X
+ * @options: AxiEthernet option word
+ * @last_link: Phy link state in which the PHY was negotiated earlier
+ * @features: Stores the extended features supported by the axienet hw
+ * @tx_bd_v: Virtual address of the TX buffer descriptor ring
+ * @tx_bd_p: Physical address(start address) of the TX buffer descr. ring
+ * @rx_bd_v: Virtual address of the RX buffer descriptor ring
+ * @rx_bd_p: Physical address(start address) of the RX buffer descr. ring
+ * @tx_bd_ci: Stores the index of the Tx buffer descriptor in the ring being
+ * accessed currently. Used while alloc. BDs before a TX starts
+ * @tx_bd_tail: Stores the index of the Tx buffer descriptor in the ring being
+ * accessed currently. Used while processing BDs after the TX
+ * completed.
+ * @rx_bd_ci: Stores the index of the Rx buffer descriptor in the ring being
+ * accessed currently.
+ * @max_frm_size: Stores the maximum size of the frame that can be that
+ * Txed/Rxed in the existing hardware. If jumbo option is
+ * supported, the maximum frame size would be 9k. Else it is
+ * 1522 bytes (assuming support for basic VLAN)
+ * @jumbo_support: Stores hardware configuration for jumbo support. If hardware
+ * can handle jumbo packets, this entry will be 1, else 0.
+ */
+struct axienet_local {
+ struct net_device *ndev;
+ struct device *dev;
+
+ /* Connection to PHY device */
+ struct phy_device *phy_dev; /* Pointer to PHY device */
+ struct device_node *phy_node;
+
+ /* MDIO bus data */
+ struct mii_bus *mii_bus; /* MII bus reference */
+ int mdio_irqs[PHY_MAX_ADDR]; /* IRQs table for MDIO bus */
+
+ /* IO registers, dma functions and IRQs */
+ void __iomem *regs;
+ void __iomem *dma_regs;
+
+ struct tasklet_struct dma_err_tasklet;
+
+ int tx_irq;
+ int rx_irq;
+ u32 temac_type;
+ u32 phy_type;
+
+ u32 options; /* Current options word */
+ u32 last_link;
+ u32 features;
+
+ /* Buffer descriptors */
+ struct axidma_bd *tx_bd_v;
+ dma_addr_t tx_bd_p;
+ struct axidma_bd *rx_bd_v;
+ dma_addr_t rx_bd_p;
+ u32 tx_bd_ci;
+ u32 tx_bd_tail;
+ u32 rx_bd_ci;
+
+ u32 max_frm_size;
+ u32 jumbo_support;
+
+ int csum_offload_on_tx_path;
+ int csum_offload_on_rx_path;
+
+ u32 coalesce_count_rx;
+ u32 coalesce_count_tx;
+};
+
+/**
+ * struct axiethernet_option - Used to set axi ethernet hardware options
+ * @opt: Option to be set.
+ * @reg: Register offset to be written for setting the option
+ * @m_or: Mask to be ORed for setting the option in the register
+ */
+struct axienet_option {
+ u32 opt;
+ u32 reg;
+ u32 m_or;
+};
+
+/**
+ * axienet_ior - Memory mapped Axi Ethernet register read
+ * @lp: Pointer to axienet local structure
+ * @offset: Address offset from the base address of Axi Ethernet core
+ *
+ * returns: The contents of the Axi Ethernet register
+ *
+ * This function returns the contents of the corresponding register.
+ */
+static inline u32 axienet_ior(struct axienet_local *lp, off_t offset)
+{
+ return in_be32(lp->regs + offset);
+}
+
+/**
+ * axienet_iow - Memory mapped Axi Ethernet register write
+ * @lp: Pointer to axienet local structure
+ * @offset: Address offset from the base address of Axi Ethernet core
+ * @value: Value to be written into the Axi Ethernet register
+ *
+ * This function writes the desired value into the corresponding Axi Ethernet
+ * register.
+ */
+static inline void axienet_iow(struct axienet_local *lp, off_t offset,
+ u32 value)
+{
+ out_be32((lp->regs + offset), value);
+}
+
+/* Function prototypes visible in xilinx_axienet_mdio.c for other files */
+int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np);
+int axienet_mdio_wait_until_ready(struct axienet_local *lp);
+void axienet_mdio_teardown(struct axienet_local *lp);
+
+#endif /* XILINX_AXI_ENET_H */
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
new file mode 100644
index 000000000000..2fcbeba6814b
--- /dev/null
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -0,0 +1,1669 @@
+/*
+ * Xilinx Axi Ethernet device driver
+ *
+ * Copyright (c) 2008 Nissin Systems Co., Ltd., Yoshio Kashiwagi
+ * Copyright (c) 2005-2008 DLA Systems, David H. Lynch Jr. <dhlii@dlasys.net>
+ * Copyright (c) 2008-2009 Secret Lab Technologies Ltd.
+ * Copyright (c) 2010 Xilinx, Inc. All rights reserved.
+ * Copyright (c) 2012 Daniel Borkmann, <daniel.borkmann@tik.ee.ethz.ch>
+ * Copyright (c) 2012 Ariane Keller, <ariane.keller@tik.ee.ethz.ch>
+ *
+ * This is a driver for the Xilinx Axi Ethernet which is used in the Virtex6
+ * and Spartan6.
+ *
+ * TODO:
+ * - Add Axi Fifo support.
+ * - Factor out Axi DMA code into separate driver.
+ * - Test and fix basic multicast filtering.
+ * - Add support for extended multicast filtering.
+ * - Test basic VLAN support.
+ * - Add support for extended VLAN support.
+ */
+
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_mdio.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/phy.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+
+#include "xilinx_axienet.h"
+
+/* Descriptors defines for Tx and Rx DMA - 2^n for the best performance */
+#define TX_BD_NUM 64
+#define RX_BD_NUM 128
+
+/* Must be shorter than length of ethtool_drvinfo.driver field to fit */
+#define DRIVER_NAME "xaxienet"
+#define DRIVER_DESCRIPTION "Xilinx Axi Ethernet driver"
+#define DRIVER_VERSION "1.00a"
+
+#define AXIENET_REGS_N 32
+
+/* Match table for of_platform binding */
+static struct of_device_id axienet_of_match[] __devinitdata = {
+ { .compatible = "xlnx,axi-ethernet-1.00.a", },
+ { .compatible = "xlnx,axi-ethernet-1.01.a", },
+ { .compatible = "xlnx,axi-ethernet-2.01.a", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, axienet_of_match);
+
+/* Option table for setting up Axi Ethernet hardware options */
+static struct axienet_option axienet_options[] = {
+ /* Turn on jumbo packet support for both Rx and Tx */
+ {
+ .opt = XAE_OPTION_JUMBO,
+ .reg = XAE_TC_OFFSET,
+ .m_or = XAE_TC_JUM_MASK,
+ }, {
+ .opt = XAE_OPTION_JUMBO,
+ .reg = XAE_RCW1_OFFSET,
+ .m_or = XAE_RCW1_JUM_MASK,
+ }, { /* Turn on VLAN packet support for both Rx and Tx */
+ .opt = XAE_OPTION_VLAN,
+ .reg = XAE_TC_OFFSET,
+ .m_or = XAE_TC_VLAN_MASK,
+ }, {
+ .opt = XAE_OPTION_VLAN,
+ .reg = XAE_RCW1_OFFSET,
+ .m_or = XAE_RCW1_VLAN_MASK,
+ }, { /* Turn on FCS stripping on receive packets */
+ .opt = XAE_OPTION_FCS_STRIP,
+ .reg = XAE_RCW1_OFFSET,
+ .m_or = XAE_RCW1_FCS_MASK,
+ }, { /* Turn on FCS insertion on transmit packets */
+ .opt = XAE_OPTION_FCS_INSERT,
+ .reg = XAE_TC_OFFSET,
+ .m_or = XAE_TC_FCS_MASK,
+ }, { /* Turn off length/type field checking on receive packets */
+ .opt = XAE_OPTION_LENTYPE_ERR,
+ .reg = XAE_RCW1_OFFSET,
+ .m_or = XAE_RCW1_LT_DIS_MASK,
+ }, { /* Turn on Rx flow control */
+ .opt = XAE_OPTION_FLOW_CONTROL,
+ .reg = XAE_FCC_OFFSET,
+ .m_or = XAE_FCC_FCRX_MASK,
+ }, { /* Turn on Tx flow control */
+ .opt = XAE_OPTION_FLOW_CONTROL,
+ .reg = XAE_FCC_OFFSET,
+ .m_or = XAE_FCC_FCTX_MASK,
+ }, { /* Turn on promiscuous frame filtering */
+ .opt = XAE_OPTION_PROMISC,
+ .reg = XAE_FMI_OFFSET,
+ .m_or = XAE_FMI_PM_MASK,
+ }, { /* Enable transmitter */
+ .opt = XAE_OPTION_TXEN,
+ .reg = XAE_TC_OFFSET,
+ .m_or = XAE_TC_TX_MASK,
+ }, { /* Enable receiver */
+ .opt = XAE_OPTION_RXEN,
+ .reg = XAE_RCW1_OFFSET,
+ .m_or = XAE_RCW1_RX_MASK,
+ },
+ {}
+};
+
+/**
+ * axienet_dma_in32 - Memory mapped Axi DMA register read
+ * @lp: Pointer to axienet local structure
+ * @reg: Address offset from the base address of the Axi DMA core
+ *
+ * returns: The contents of the Axi DMA register
+ *
+ * This function returns the contents of the corresponding Axi DMA register.
+ */
+static inline u32 axienet_dma_in32(struct axienet_local *lp, off_t reg)
+{
+ return in_be32(lp->dma_regs + reg);
+}
+
+/**
+ * axienet_dma_out32 - Memory mapped Axi DMA register write.
+ * @lp: Pointer to axienet local structure
+ * @reg: Address offset from the base address of the Axi DMA core
+ * @value: Value to be written into the Axi DMA register
+ *
+ * This function writes the desired value into the corresponding Axi DMA
+ * register.
+ */
+static inline void axienet_dma_out32(struct axienet_local *lp,
+ off_t reg, u32 value)
+{
+ out_be32((lp->dma_regs + reg), value);
+}
+
+/**
+ * axienet_dma_bd_release - Release buffer descriptor rings
+ * @ndev: Pointer to the net_device structure
+ *
+ * This function is used to release the descriptors allocated in
+ * axienet_dma_bd_init. axienet_dma_bd_release is called when Axi Ethernet
+ * driver stop api is called.
+ */
+static void axienet_dma_bd_release(struct net_device *ndev)
+{
+ int i;
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ for (i = 0; i < RX_BD_NUM; i++) {
+ dma_unmap_single(ndev->dev.parent, lp->rx_bd_v[i].phys,
+ lp->max_frm_size, DMA_FROM_DEVICE);
+ dev_kfree_skb((struct sk_buff *)
+ (lp->rx_bd_v[i].sw_id_offset));
+ }
+
+ if (lp->rx_bd_v) {
+ dma_free_coherent(ndev->dev.parent,
+ sizeof(*lp->rx_bd_v) * RX_BD_NUM,
+ lp->rx_bd_v,
+ lp->rx_bd_p);
+ }
+ if (lp->tx_bd_v) {
+ dma_free_coherent(ndev->dev.parent,
+ sizeof(*lp->tx_bd_v) * TX_BD_NUM,
+ lp->tx_bd_v,
+ lp->tx_bd_p);
+ }
+}
+
+/**
+ * axienet_dma_bd_init - Setup buffer descriptor rings for Axi DMA
+ * @ndev: Pointer to the net_device structure
+ *
+ * returns: 0, on success
+ * -ENOMEM, on failure
+ *
+ * This function is called to initialize the Rx and Tx DMA descriptor
+ * rings. This initializes the descriptors with required default values
+ * and is called when Axi Ethernet driver reset is called.
+ */
+static int axienet_dma_bd_init(struct net_device *ndev)
+{
+ u32 cr;
+ int i;
+ struct sk_buff *skb;
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ /* 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;
+
+ /*
+ * Allocate the Tx and Rx buffer descriptors.
+ */
+ lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent,
+ sizeof(*lp->tx_bd_v) * TX_BD_NUM,
+ &lp->tx_bd_p,
+ GFP_KERNEL);
+ if (!lp->tx_bd_v) {
+ dev_err(&ndev->dev, "unable to allocate DMA Tx buffer "
+ "descriptors");
+ goto out;
+ }
+
+ lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent,
+ sizeof(*lp->rx_bd_v) * RX_BD_NUM,
+ &lp->rx_bd_p,
+ GFP_KERNEL);
+ if (!lp->rx_bd_v) {
+ dev_err(&ndev->dev, "unable to allocate DMA Rx buffer "
+ "descriptors");
+ goto out;
+ }
+
+ memset(lp->tx_bd_v, 0, sizeof(*lp->tx_bd_v) * TX_BD_NUM);
+ for (i = 0; i < TX_BD_NUM; i++) {
+ lp->tx_bd_v[i].next = lp->tx_bd_p +
+ sizeof(*lp->tx_bd_v) *
+ ((i + 1) % TX_BD_NUM);
+ }
+
+ memset(lp->rx_bd_v, 0, sizeof(*lp->rx_bd_v) * RX_BD_NUM);
+ for (i = 0; i < RX_BD_NUM; i++) {
+ lp->rx_bd_v[i].next = lp->rx_bd_p +
+ sizeof(*lp->rx_bd_v) *
+ ((i + 1) % RX_BD_NUM);
+
+ skb = netdev_alloc_skb_ip_align(ndev, lp->max_frm_size);
+ if (!skb) {
+ dev_err(&ndev->dev, "alloc_skb error %d\n", i);
+ goto out;
+ }
+
+ lp->rx_bd_v[i].sw_id_offset = (u32) skb;
+ lp->rx_bd_v[i].phys = dma_map_single(ndev->dev.parent,
+ skb->data,
+ lp->max_frm_size,
+ DMA_FROM_DEVICE);
+ lp->rx_bd_v[i].cntrl = lp->max_frm_size;
+ }
+
+ /* Start updating the Rx channel control register */
+ cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
+ /* Update the interrupt coalesce count */
+ cr = ((cr & ~XAXIDMA_COALESCE_MASK) |
+ ((lp->coalesce_count_rx) << XAXIDMA_COALESCE_SHIFT));
+ /* Update the delay timer count */
+ cr = ((cr & ~XAXIDMA_DELAY_MASK) |
+ (XAXIDMA_DFT_RX_WAITBOUND << XAXIDMA_DELAY_SHIFT));
+ /* Enable coalesce, delay timer and error interrupts */
+ cr |= XAXIDMA_IRQ_ALL_MASK;
+ /* Write to the Rx channel control register */
+ axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
+
+ /* Start updating the Tx channel control register */
+ cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
+ /* Update the interrupt coalesce count */
+ cr = (((cr & ~XAXIDMA_COALESCE_MASK)) |
+ ((lp->coalesce_count_tx) << XAXIDMA_COALESCE_SHIFT));
+ /* Update the delay timer count */
+ cr = (((cr & ~XAXIDMA_DELAY_MASK)) |
+ (XAXIDMA_DFT_TX_WAITBOUND << XAXIDMA_DELAY_SHIFT));
+ /* Enable coalesce, delay timer and error interrupts */
+ cr |= XAXIDMA_IRQ_ALL_MASK;
+ /* Write to the Tx channel control register */
+ 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.*/
+ 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,
+ cr | XAXIDMA_CR_RUNSTOP_MASK);
+ axienet_dma_out32(lp, XAXIDMA_RX_TDESC_OFFSET, lp->rx_bd_p +
+ (sizeof(*lp->rx_bd_v) * (RX_BD_NUM - 1)));
+
+ /* 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 */
+ 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,
+ cr | XAXIDMA_CR_RUNSTOP_MASK);
+
+ return 0;
+out:
+ axienet_dma_bd_release(ndev);
+ return -ENOMEM;
+}
+
+/**
+ * axienet_set_mac_address - Write the MAC address
+ * @ndev: Pointer to the net_device structure
+ * @address: 6 byte Address to be written as MAC address
+ *
+ * This function is called to initialize the MAC address of the Axi Ethernet
+ * core. It writes to the UAW0 and UAW1 registers of the core.
+ */
+static void axienet_set_mac_address(struct net_device *ndev, void *address)
+{
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ if (address)
+ memcpy(ndev->dev_addr, address, ETH_ALEN);
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ random_ether_addr(ndev->dev_addr);
+
+ /* Set up unicast MAC address filter set its mac address */
+ axienet_iow(lp, XAE_UAW0_OFFSET,
+ (ndev->dev_addr[0]) |
+ (ndev->dev_addr[1] << 8) |
+ (ndev->dev_addr[2] << 16) |
+ (ndev->dev_addr[3] << 24));
+ axienet_iow(lp, XAE_UAW1_OFFSET,
+ (((axienet_ior(lp, XAE_UAW1_OFFSET)) &
+ ~XAE_UAW1_UNICASTADDR_MASK) |
+ (ndev->dev_addr[4] |
+ (ndev->dev_addr[5] << 8))));
+}
+
+/**
+ * netdev_set_mac_address - Write the MAC address (from outside the driver)
+ * @ndev: Pointer to the net_device structure
+ * @p: 6 byte Address to be written as MAC address
+ *
+ * returns: 0 for all conditions. Presently, there is no failure case.
+ *
+ * This function is called to initialize the MAC address of the Axi Ethernet
+ * core. It calls the core specific axienet_set_mac_address. This is the
+ * function that goes into net_device_ops structure entry ndo_set_mac_address.
+ */
+static int netdev_set_mac_address(struct net_device *ndev, void *p)
+{
+ struct sockaddr *addr = p;
+ axienet_set_mac_address(ndev, addr->sa_data);
+ return 0;
+}
+
+/**
+ * axienet_set_multicast_list - Prepare the multicast table
+ * @ndev: Pointer to the net_device structure
+ *
+ * This function is called to initialize the multicast table during
+ * initialization. The Axi Ethernet basic multicast support has a four-entry
+ * multicast table which is initialized here. Additionally this function
+ * goes into the net_device_ops structure entry ndo_set_multicast_list. This
+ * means whenever the multicast table entries need to be updated this
+ * function gets called.
+ */
+static void axienet_set_multicast_list(struct net_device *ndev)
+{
+ int i;
+ u32 reg, af0reg, af1reg;
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ if (ndev->flags & (IFF_ALLMULTI | IFF_PROMISC) ||
+ 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. */
+ ndev->flags |= IFF_PROMISC;
+ reg = axienet_ior(lp, XAE_FMI_OFFSET);
+ reg |= XAE_FMI_PM_MASK;
+ axienet_iow(lp, XAE_FMI_OFFSET, reg);
+ dev_info(&ndev->dev, "Promiscuous mode enabled.\n");
+ } else if (!netdev_mc_empty(ndev)) {
+ struct netdev_hw_addr *ha;
+
+ i = 0;
+ netdev_for_each_mc_addr(ha, ndev) {
+ if (i >= XAE_MULTICAST_CAM_TABLE_NUM)
+ break;
+
+ af0reg = (ha->addr[0]);
+ af0reg |= (ha->addr[1] << 8);
+ af0reg |= (ha->addr[2] << 16);
+ af0reg |= (ha->addr[3] << 24);
+
+ af1reg = (ha->addr[4]);
+ af1reg |= (ha->addr[5] << 8);
+
+ reg = axienet_ior(lp, XAE_FMI_OFFSET) & 0xFFFFFF00;
+ reg |= i;
+
+ axienet_iow(lp, XAE_FMI_OFFSET, reg);
+ axienet_iow(lp, XAE_AF0_OFFSET, af0reg);
+ axienet_iow(lp, XAE_AF1_OFFSET, af1reg);
+ i++;
+ }
+ } else {
+ reg = axienet_ior(lp, XAE_FMI_OFFSET);
+ reg &= ~XAE_FMI_PM_MASK;
+
+ axienet_iow(lp, XAE_FMI_OFFSET, reg);
+
+ for (i = 0; i < XAE_MULTICAST_CAM_TABLE_NUM; i++) {
+ reg = axienet_ior(lp, XAE_FMI_OFFSET) & 0xFFFFFF00;
+ reg |= i;
+
+ axienet_iow(lp, XAE_FMI_OFFSET, reg);
+ axienet_iow(lp, XAE_AF0_OFFSET, 0);
+ axienet_iow(lp, XAE_AF1_OFFSET, 0);
+ }
+
+ dev_info(&ndev->dev, "Promiscuous mode disabled.\n");
+ }
+}
+
+/**
+ * axienet_setoptions - Set an Axi Ethernet option
+ * @ndev: Pointer to the net_device structure
+ * @options: Option to be enabled/disabled
+ *
+ * The Axi Ethernet core has multiple features which can be selectively turned
+ * on or off. The typical options could be jumbo frame option, basic VLAN
+ * option, promiscuous mode option etc. This function is used to set or clear
+ * these options in the Axi Ethernet hardware. This is done through
+ * axienet_option structure .
+ */
+static void axienet_setoptions(struct net_device *ndev, u32 options)
+{
+ int reg;
+ struct axienet_local *lp = netdev_priv(ndev);
+ struct axienet_option *tp = &axienet_options[0];
+
+ while (tp->opt) {
+ reg = ((axienet_ior(lp, tp->reg)) & ~(tp->m_or));
+ if (options & tp->opt)
+ reg |= tp->m_or;
+ axienet_iow(lp, tp->reg, reg);
+ tp++;
+ }
+
+ lp->options |= options;
+}
+
+static void __axienet_device_reset(struct axienet_local *lp,
+ struct device *dev, off_t offset)
+{
+ u32 timeout;
+ /* 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. */
+ 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");
+ break;
+ }
+ }
+}
+
+/**
+ * axienet_device_reset - Reset and initialize the Axi Ethernet hardware.
+ * @ndev: Pointer to the net_device structure
+ *
+ * This function is called to reset and initialize the Axi Ethernet core. This
+ * is typically called during initialization. It does a reset of the Axi DMA
+ * Rx/Tx channels and initializes the Axi DMA BDs. Since Axi DMA reset lines
+ * areconnected to Axi Ethernet reset lines, this in turn resets the Axi
+ * Ethernet core. No separate hardware reset is done for the Axi Ethernet
+ * core.
+ */
+static void axienet_device_reset(struct net_device *ndev)
+{
+ u32 axienet_status;
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ __axienet_device_reset(lp, &ndev->dev, XAXIDMA_TX_CR_OFFSET);
+ __axienet_device_reset(lp, &ndev->dev, XAXIDMA_RX_CR_OFFSET);
+
+ lp->max_frm_size = XAE_MAX_VLAN_FRAME_SIZE;
+ 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;
+ }
+
+ if (axienet_dma_bd_init(ndev)) {
+ dev_err(&ndev->dev, "axienet_device_reset descriptor "
+ "allocation failed\n");
+ }
+
+ axienet_status = axienet_ior(lp, XAE_RCW1_OFFSET);
+ axienet_status &= ~XAE_RCW1_RX_MASK;
+ axienet_iow(lp, XAE_RCW1_OFFSET, axienet_status);
+
+ axienet_status = axienet_ior(lp, XAE_IP_OFFSET);
+ if (axienet_status & XAE_INT_RXRJECT_MASK)
+ axienet_iow(lp, XAE_IS_OFFSET, XAE_INT_RXRJECT_MASK);
+
+ axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
+
+ /* Sync default options with HW but leave receiver and
+ * transmitter disabled.*/
+ axienet_setoptions(ndev, lp->options &
+ ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
+ axienet_set_mac_address(ndev, NULL);
+ axienet_set_multicast_list(ndev);
+ axienet_setoptions(ndev, lp->options);
+
+ ndev->trans_start = jiffies;
+}
+
+/**
+ * axienet_adjust_link - Adjust the PHY link speed/duplex.
+ * @ndev: Pointer to the net_device structure
+ *
+ * This function is called to change the speed and duplex setting after
+ * auto negotiation is done by the PHY. This is the function that gets
+ * registered with the PHY interface through the "of_phy_connect" call.
+ */
+static void axienet_adjust_link(struct net_device *ndev)
+{
+ u32 emmc_reg;
+ u32 link_state;
+ u32 setspeed = 1;
+ struct axienet_local *lp = netdev_priv(ndev);
+ struct phy_device *phy = lp->phy_dev;
+
+ link_state = phy->speed | (phy->duplex << 1) | phy->link;
+ if (lp->last_link != link_state) {
+ if ((phy->speed == SPEED_10) || (phy->speed == SPEED_100)) {
+ if (lp->phy_type == XAE_PHY_TYPE_1000BASE_X)
+ setspeed = 0;
+ } else {
+ if ((phy->speed == SPEED_1000) &&
+ (lp->phy_type == XAE_PHY_TYPE_MII))
+ setspeed = 0;
+ }
+
+ if (setspeed == 1) {
+ emmc_reg = axienet_ior(lp, XAE_EMMC_OFFSET);
+ emmc_reg &= ~XAE_EMMC_LINKSPEED_MASK;
+
+ switch (phy->speed) {
+ case SPEED_1000:
+ emmc_reg |= XAE_EMMC_LINKSPD_1000;
+ break;
+ case SPEED_100:
+ emmc_reg |= XAE_EMMC_LINKSPD_100;
+ break;
+ case SPEED_10:
+ emmc_reg |= XAE_EMMC_LINKSPD_10;
+ break;
+ default:
+ dev_err(&ndev->dev, "Speed other than 10, 100 "
+ "or 1Gbps is not supported\n");
+ break;
+ }
+
+ axienet_iow(lp, XAE_EMMC_OFFSET, emmc_reg);
+ lp->last_link = link_state;
+ phy_print_status(phy);
+ } else {
+ dev_err(&ndev->dev, "Error setting Axi Ethernet "
+ "mac speed\n");
+ }
+ }
+}
+
+/**
+ * axienet_start_xmit_done - Invoked once a transmit is completed by the
+ * Axi DMA Tx channel.
+ * @ndev: Pointer to the net_device structure
+ *
+ * This function is invoked from the Axi DMA Tx isr to notify the completion
+ * of transmit operation. It clears fields in the corresponding Tx BDs and
+ * unmaps the corresponding buffer so that CPU can regain ownership of the
+ * buffer. It finally invokes "netif_wake_queue" to restart transmission if
+ * required.
+ */
+static void axienet_start_xmit_done(struct net_device *ndev)
+{
+ u32 size = 0;
+ u32 packets = 0;
+ struct axienet_local *lp = netdev_priv(ndev);
+ struct axidma_bd *cur_p;
+ unsigned int status = 0;
+
+ cur_p = &lp->tx_bd_v[lp->tx_bd_ci];
+ status = cur_p->status;
+ while (status & XAXIDMA_BD_STS_COMPLETE_MASK) {
+ dma_unmap_single(ndev->dev.parent, cur_p->phys,
+ (cur_p->cntrl & XAXIDMA_BD_CTRL_LENGTH_MASK),
+ DMA_TO_DEVICE);
+ if (cur_p->app4)
+ dev_kfree_skb_irq((struct sk_buff *)cur_p->app4);
+ /*cur_p->phys = 0;*/
+ cur_p->app0 = 0;
+ cur_p->app1 = 0;
+ cur_p->app2 = 0;
+ cur_p->app4 = 0;
+ cur_p->status = 0;
+
+ size += status & XAXIDMA_BD_STS_ACTUAL_LEN_MASK;
+ packets++;
+
+ 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;
+ }
+
+ ndev->stats.tx_packets += packets;
+ ndev->stats.tx_bytes += size;
+ netif_wake_queue(ndev);
+}
+
+/**
+ * axienet_check_tx_bd_space - Checks if a BD/group of BDs are currently busy
+ * @lp: Pointer to the axienet_local structure
+ * @num_frag: The number of BDs to check for
+ *
+ * returns: 0, on success
+ * NETDEV_TX_BUSY, if any of the descriptors are not free
+ *
+ * This function is invoked before BDs are allocated and transmission starts.
+ * This function returns 0 if a BD or group of BDs can be allocated for
+ * transmission. If the BD or any of the BDs are not free the function
+ * returns a busy status. This is invoked from axienet_start_xmit.
+ */
+static inline int axienet_check_tx_bd_space(struct axienet_local *lp,
+ int num_frag)
+{
+ struct axidma_bd *cur_p;
+ cur_p = &lp->tx_bd_v[(lp->tx_bd_tail + num_frag) % TX_BD_NUM];
+ if (cur_p->status & XAXIDMA_BD_STS_ALL_MASK)
+ return NETDEV_TX_BUSY;
+ return 0;
+}
+
+/**
+ * axienet_start_xmit - Starts the transmission.
+ * @skb: sk_buff pointer that contains data to be Txed.
+ * @ndev: Pointer to net_device structure.
+ *
+ * returns: NETDEV_TX_OK, on success
+ * NETDEV_TX_BUSY, if any of the descriptors are not free
+ *
+ * This function is invoked from upper layers to initiate transmission. The
+ * function uses the next available free BDs and populates their fields to
+ * start the transmission. Additionally if checksum offloading is supported,
+ * it populates AXI Stream Control fields with appropriate values.
+ */
+static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ u32 ii;
+ u32 num_frag;
+ u32 csum_start_off;
+ u32 csum_index_off;
+ skb_frag_t *frag;
+ dma_addr_t tail_p;
+ struct axienet_local *lp = netdev_priv(ndev);
+ struct axidma_bd *cur_p;
+
+ num_frag = skb_shinfo(skb)->nr_frags;
+ cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
+
+ if (axienet_check_tx_bd_space(lp, num_frag)) {
+ if (!netif_queue_stopped(ndev))
+ netif_stop_queue(ndev);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ if (lp->features & XAE_FEATURE_FULL_TX_CSUM) {
+ /* Tx Full Checksum Offload Enabled */
+ cur_p->app0 |= 2;
+ } else if (lp->features & XAE_FEATURE_PARTIAL_RX_CSUM) {
+ csum_start_off = skb_transport_offset(skb);
+ csum_index_off = csum_start_off + skb->csum_offset;
+ /* Tx Partial Checksum Offload Enabled */
+ cur_p->app0 |= 1;
+ cur_p->app1 = (csum_start_off << 16) | csum_index_off;
+ }
+ } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
+ cur_p->app0 |= 2; /* Tx Full Checksum Offload Enabled */
+ }
+
+ cur_p->cntrl = skb_headlen(skb) | XAXIDMA_BD_CTRL_TXSOF_MASK;
+ cur_p->phys = dma_map_single(ndev->dev.parent, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
+
+ for (ii = 0; ii < num_frag; ii++) {
+ 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,
+ skb_frag_address(frag),
+ skb_frag_size(frag),
+ DMA_TO_DEVICE);
+ cur_p->cntrl = skb_frag_size(frag);
+ }
+
+ cur_p->cntrl |= XAXIDMA_BD_CTRL_TXEOF_MASK;
+ cur_p->app4 = (unsigned long)skb;
+
+ tail_p = lp->tx_bd_p + sizeof(*lp->tx_bd_v) * lp->tx_bd_tail;
+ /* Start the transfer */
+ axienet_dma_out32(lp, XAXIDMA_TX_TDESC_OFFSET, tail_p);
+ lp->tx_bd_tail = ++lp->tx_bd_tail % TX_BD_NUM;
+
+ return NETDEV_TX_OK;
+}
+
+/**
+ * axienet_recv - Is called from Axi DMA Rx Isr to complete the received
+ * BD processing.
+ * @ndev: Pointer to net_device structure.
+ *
+ * This function is invoked from the Axi DMA Rx isr to process the Rx BDs. It
+ * does minimal processing and invokes "netif_rx" to complete further
+ * processing.
+ */
+static void axienet_recv(struct net_device *ndev)
+{
+ u32 length;
+ u32 csumstatus;
+ u32 size = 0;
+ u32 packets = 0;
+ dma_addr_t tail_p;
+ 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;
+ cur_p = &lp->rx_bd_v[lp->rx_bd_ci];
+
+ while ((cur_p->status & XAXIDMA_BD_STS_COMPLETE_MASK)) {
+ skb = (struct sk_buff *) (cur_p->sw_id_offset);
+ length = cur_p->app4 & 0x0000FFFF;
+
+ dma_unmap_single(ndev->dev.parent, cur_p->phys,
+ lp->max_frm_size,
+ DMA_FROM_DEVICE);
+
+ skb_put(skb, length);
+ skb->protocol = eth_type_trans(skb, ndev);
+ /*skb_checksum_none_assert(skb);*/
+ skb->ip_summed = CHECKSUM_NONE;
+
+ /* if we're doing Rx csum offload, set it up */
+ if (lp->features & XAE_FEATURE_FULL_RX_CSUM) {
+ csumstatus = (cur_p->app2 &
+ XAE_FULL_CSUM_STATUS_MASK) >> 3;
+ if ((csumstatus == XAE_IP_TCP_CSUM_VALIDATED) ||
+ (csumstatus == XAE_IP_UDP_CSUM_VALIDATED)) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ }
+ } else if ((lp->features & XAE_FEATURE_PARTIAL_RX_CSUM) != 0 &&
+ skb->protocol == __constant_htons(ETH_P_IP) &&
+ skb->len > 64) {
+ skb->csum = be32_to_cpu(cur_p->app3 & 0xFFFF);
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ }
+
+ netif_rx(skb);
+
+ size += length;
+ packets++;
+
+ new_skb = netdev_alloc_skb_ip_align(ndev, lp->max_frm_size);
+ if (!new_skb) {
+ dev_err(&ndev->dev, "no memory for new sk_buff\n");
+ return;
+ }
+ cur_p->phys = dma_map_single(ndev->dev.parent, new_skb->data,
+ lp->max_frm_size,
+ DMA_FROM_DEVICE);
+ cur_p->cntrl = lp->max_frm_size;
+ cur_p->status = 0;
+ cur_p->sw_id_offset = (u32) new_skb;
+
+ 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);
+}
+
+/**
+ * axienet_tx_irq - Tx Done Isr.
+ * @irq: irq number
+ * @_ndev: net_device pointer
+ *
+ * returns: IRQ_HANDLED for all cases.
+ *
+ * This is the Axi DMA Tx done Isr. It invokes "axienet_start_xmit_done"
+ * to complete the BD processing.
+ */
+static irqreturn_t axienet_tx_irq(int irq, void *_ndev)
+{
+ u32 cr;
+ unsigned int status;
+ struct net_device *ndev = _ndev;
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ status = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET);
+ if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) {
+ axienet_start_xmit_done(lp->ndev);
+ goto out;
+ }
+ if (!(status & XAXIDMA_IRQ_ALL_MASK))
+ dev_err(&ndev->dev, "No interrupts asserted in Tx path");
+ if (status & XAXIDMA_IRQ_ERROR_MASK) {
+ dev_err(&ndev->dev, "DMA Tx error 0x%x\n", status);
+ dev_err(&ndev->dev, "Current BD is at: 0x%x\n",
+ (lp->tx_bd_v[lp->tx_bd_ci]).phys);
+
+ cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
+ /* Disable coalesce, delay timer and error interrupts */
+ cr &= (~XAXIDMA_IRQ_ALL_MASK);
+ /* Write to the Tx channel control register */
+ axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr);
+
+ cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
+ /* Disable coalesce, delay timer and error interrupts */
+ cr &= (~XAXIDMA_IRQ_ALL_MASK);
+ /* Write to the Rx channel control register */
+ axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
+
+ tasklet_schedule(&lp->dma_err_tasklet);
+ }
+out:
+ axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status);
+ return IRQ_HANDLED;
+}
+
+/**
+ * axienet_rx_irq - Rx Isr.
+ * @irq: irq number
+ * @_ndev: net_device pointer
+ *
+ * returns: IRQ_HANDLED for all cases.
+ *
+ * This is the Axi DMA Rx Isr. It invokes "axienet_recv" to complete the BD
+ * processing.
+ */
+static irqreturn_t axienet_rx_irq(int irq, void *_ndev)
+{
+ u32 cr;
+ unsigned int status;
+ struct net_device *ndev = _ndev;
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ status = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET);
+ if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) {
+ axienet_recv(lp->ndev);
+ goto out;
+ }
+ if (!(status & XAXIDMA_IRQ_ALL_MASK))
+ dev_err(&ndev->dev, "No interrupts asserted in Rx path");
+ if (status & XAXIDMA_IRQ_ERROR_MASK) {
+ dev_err(&ndev->dev, "DMA Rx error 0x%x\n", status);
+ dev_err(&ndev->dev, "Current BD is at: 0x%x\n",
+ (lp->rx_bd_v[lp->rx_bd_ci]).phys);
+
+ cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
+ /* Disable coalesce, delay timer and error interrupts */
+ cr &= (~XAXIDMA_IRQ_ALL_MASK);
+ /* Finally write to the Tx channel control register */
+ axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr);
+
+ cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
+ /* Disable coalesce, delay timer and error interrupts */
+ cr &= (~XAXIDMA_IRQ_ALL_MASK);
+ /* write to the Rx channel control register */
+ axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
+
+ tasklet_schedule(&lp->dma_err_tasklet);
+ }
+out:
+ axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status);
+ return IRQ_HANDLED;
+}
+
+/**
+ * axienet_open - Driver open routine.
+ * @ndev: Pointer to net_device structure
+ *
+ * returns: 0, on success.
+ * -ENODEV, if PHY cannot be connected to
+ * non-zero error value on failure
+ *
+ * This is the driver open routine. It calls phy_start to start the PHY device.
+ * It also allocates interrupt service routines, enables the interrupt lines
+ * and ISR handling. Axi Ethernet core is reset through Axi DMA core. Buffer
+ * descriptors are initialized.
+ */
+static int axienet_open(struct net_device *ndev)
+{
+ int ret, mdio_mcreg;
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ dev_dbg(&ndev->dev, "axienet_open()\n");
+
+ mdio_mcreg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
+ ret = axienet_mdio_wait_until_ready(lp);
+ if (ret < 0)
+ return ret;
+ /* 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. */
+ axienet_iow(lp, XAE_MDIO_MC_OFFSET,
+ (mdio_mcreg & (~XAE_MDIO_MC_MDIOEN_MASK)));
+ axienet_device_reset(ndev);
+ /* Enable the MDIO */
+ axienet_iow(lp, XAE_MDIO_MC_OFFSET, mdio_mcreg);
+ ret = axienet_mdio_wait_until_ready(lp);
+ if (ret < 0)
+ return ret;
+
+ if (lp->phy_node) {
+ 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;
+ }
+ phy_start(lp->phy_dev);
+ }
+
+ /* Enable interrupts for Axi DMA Tx */
+ ret = request_irq(lp->tx_irq, axienet_tx_irq, 0, ndev->name, ndev);
+ if (ret)
+ goto err_tx_irq;
+ /* Enable interrupts for Axi DMA Rx */
+ ret = request_irq(lp->rx_irq, axienet_rx_irq, 0, ndev->name, ndev);
+ if (ret)
+ goto err_rx_irq;
+ /* Enable tasklets for Axi DMA error handling */
+ tasklet_enable(&lp->dma_err_tasklet);
+ return 0;
+
+err_rx_irq:
+ free_irq(lp->tx_irq, ndev);
+err_tx_irq:
+ if (lp->phy_dev)
+ phy_disconnect(lp->phy_dev);
+ lp->phy_dev = NULL;
+ dev_err(lp->dev, "request_irq() failed\n");
+ return ret;
+}
+
+/**
+ * axienet_stop - Driver stop routine.
+ * @ndev: Pointer to net_device structure
+ *
+ * returns: 0, on success.
+ *
+ * This is the driver stop routine. It calls phy_disconnect to stop the PHY
+ * device. It also removes the interrupt handlers and disables the interrupts.
+ * The Axi DMA Tx/Rx BDs are released.
+ */
+static int axienet_stop(struct net_device *ndev)
+{
+ u32 cr;
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ dev_dbg(&ndev->dev, "axienet_close()\n");
+
+ cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
+ axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET,
+ cr & (~XAXIDMA_CR_RUNSTOP_MASK));
+ cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
+ axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET,
+ cr & (~XAXIDMA_CR_RUNSTOP_MASK));
+ axienet_setoptions(ndev, lp->options &
+ ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
+
+ tasklet_disable(&lp->dma_err_tasklet);
+
+ free_irq(lp->tx_irq, ndev);
+ free_irq(lp->rx_irq, ndev);
+
+ if (lp->phy_dev)
+ phy_disconnect(lp->phy_dev);
+ lp->phy_dev = NULL;
+
+ axienet_dma_bd_release(ndev);
+ return 0;
+}
+
+/**
+ * axienet_change_mtu - Driver change mtu routine.
+ * @ndev: Pointer to net_device structure
+ * @new_mtu: New mtu value to be applied
+ *
+ * returns: Always returns 0 (success).
+ *
+ * This is the change mtu driver routine. It checks if the Axi Ethernet
+ * hardware supports jumbo frames before changing the mtu. This can be
+ * called only when the device is not up.
+ */
+static int axienet_change_mtu(struct net_device *ndev, int new_mtu)
+{
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ 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;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/**
+ * axienet_poll_controller - Axi Ethernet poll mechanism.
+ * @ndev: Pointer to net_device structure
+ *
+ * This implements Rx/Tx ISR poll mechanisms. The interrupts are disabled prior
+ * to polling the ISRs and are enabled back after the polling is done.
+ */
+static void axienet_poll_controller(struct net_device *ndev)
+{
+ struct axienet_local *lp = netdev_priv(ndev);
+ disable_irq(lp->tx_irq);
+ disable_irq(lp->rx_irq);
+ axienet_rx_irq(lp->tx_irq, ndev);
+ axienet_tx_irq(lp->rx_irq, ndev);
+ enable_irq(lp->tx_irq);
+ enable_irq(lp->rx_irq);
+}
+#endif
+
+static const struct net_device_ops axienet_netdev_ops = {
+ .ndo_open = axienet_open,
+ .ndo_stop = axienet_stop,
+ .ndo_start_xmit = axienet_start_xmit,
+ .ndo_change_mtu = axienet_change_mtu,
+ .ndo_set_mac_address = netdev_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_rx_mode = axienet_set_multicast_list,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = axienet_poll_controller,
+#endif
+};
+
+/**
+ * axienet_ethtools_get_settings - Get Axi Ethernet settings related to PHY.
+ * @ndev: Pointer to net_device structure
+ * @ecmd: Pointer to ethtool_cmd structure
+ *
+ * This implements ethtool command for getting PHY settings. If PHY could
+ * not be found, the function returns -ENODEV. This function calls the
+ * relevant PHY ethtool API to get the PHY settings.
+ * Issue "ethtool ethX" under linux prompt to execute this function.
+ */
+static int axienet_ethtools_get_settings(struct net_device *ndev,
+ struct ethtool_cmd *ecmd)
+{
+ struct axienet_local *lp = netdev_priv(ndev);
+ struct phy_device *phydev = lp->phy_dev;
+ if (!phydev)
+ return -ENODEV;
+ return phy_ethtool_gset(phydev, ecmd);
+}
+
+/**
+ * axienet_ethtools_set_settings - Set PHY settings as passed in the argument.
+ * @ndev: Pointer to net_device structure
+ * @ecmd: Pointer to ethtool_cmd structure
+ *
+ * This implements ethtool command for setting various PHY settings. If PHY
+ * could not be found, the function returns -ENODEV. This function calls the
+ * relevant PHY ethtool API to set the PHY.
+ * Issue e.g. "ethtool -s ethX speed 1000" under linux prompt to execute this
+ * function.
+ */
+static int axienet_ethtools_set_settings(struct net_device *ndev,
+ struct ethtool_cmd *ecmd)
+{
+ struct axienet_local *lp = netdev_priv(ndev);
+ struct phy_device *phydev = lp->phy_dev;
+ if (!phydev)
+ return -ENODEV;
+ return phy_ethtool_sset(phydev, ecmd);
+}
+
+/**
+ * axienet_ethtools_get_drvinfo - Get various Axi Ethernet driver information.
+ * @ndev: Pointer to net_device structure
+ * @ed: Pointer to ethtool_drvinfo structure
+ *
+ * This implements ethtool command for getting the driver information.
+ * Issue "ethtool -i ethX" under linux prompt to execute this function.
+ */
+static void axienet_ethtools_get_drvinfo(struct net_device *ndev,
+ struct ethtool_drvinfo *ed)
+{
+ memset(ed, 0, sizeof(struct ethtool_drvinfo));
+ strcpy(ed->driver, DRIVER_NAME);
+ strcpy(ed->version, DRIVER_VERSION);
+ ed->regdump_len = sizeof(u32) * AXIENET_REGS_N;
+}
+
+/**
+ * axienet_ethtools_get_regs_len - Get the total regs length present in the
+ * AxiEthernet core.
+ * @ndev: Pointer to net_device structure
+ *
+ * This implements ethtool command for getting the total register length
+ * information.
+ */
+static int axienet_ethtools_get_regs_len(struct net_device *ndev)
+{
+ return sizeof(u32) * AXIENET_REGS_N;
+}
+
+/**
+ * axienet_ethtools_get_regs - Dump the contents of all registers present
+ * in AxiEthernet core.
+ * @ndev: Pointer to net_device structure
+ * @regs: Pointer to ethtool_regs structure
+ * @ret: Void pointer used to return the contents of the registers.
+ *
+ * This implements ethtool command for getting the Axi Ethernet register dump.
+ * Issue "ethtool -d ethX" to execute this function.
+ */
+static void axienet_ethtools_get_regs(struct net_device *ndev,
+ struct ethtool_regs *regs, void *ret)
+{
+ u32 *data = (u32 *) ret;
+ size_t len = sizeof(u32) * AXIENET_REGS_N;
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ regs->version = 0;
+ regs->len = len;
+
+ memset(data, 0, len);
+ data[0] = axienet_ior(lp, XAE_RAF_OFFSET);
+ data[1] = axienet_ior(lp, XAE_TPF_OFFSET);
+ data[2] = axienet_ior(lp, XAE_IFGP_OFFSET);
+ data[3] = axienet_ior(lp, XAE_IS_OFFSET);
+ data[4] = axienet_ior(lp, XAE_IP_OFFSET);
+ data[5] = axienet_ior(lp, XAE_IE_OFFSET);
+ data[6] = axienet_ior(lp, XAE_TTAG_OFFSET);
+ data[7] = axienet_ior(lp, XAE_RTAG_OFFSET);
+ data[8] = axienet_ior(lp, XAE_UAWL_OFFSET);
+ data[9] = axienet_ior(lp, XAE_UAWU_OFFSET);
+ data[10] = axienet_ior(lp, XAE_TPID0_OFFSET);
+ data[11] = axienet_ior(lp, XAE_TPID1_OFFSET);
+ data[12] = axienet_ior(lp, XAE_PPST_OFFSET);
+ data[13] = axienet_ior(lp, XAE_RCW0_OFFSET);
+ data[14] = axienet_ior(lp, XAE_RCW1_OFFSET);
+ data[15] = axienet_ior(lp, XAE_TC_OFFSET);
+ data[16] = axienet_ior(lp, XAE_FCC_OFFSET);
+ data[17] = axienet_ior(lp, XAE_EMMC_OFFSET);
+ data[18] = axienet_ior(lp, XAE_PHYC_OFFSET);
+ data[19] = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
+ data[20] = axienet_ior(lp, XAE_MDIO_MCR_OFFSET);
+ data[21] = axienet_ior(lp, XAE_MDIO_MWD_OFFSET);
+ data[22] = axienet_ior(lp, XAE_MDIO_MRD_OFFSET);
+ data[23] = axienet_ior(lp, XAE_MDIO_MIS_OFFSET);
+ data[24] = axienet_ior(lp, XAE_MDIO_MIP_OFFSET);
+ data[25] = axienet_ior(lp, XAE_MDIO_MIE_OFFSET);
+ data[26] = axienet_ior(lp, XAE_MDIO_MIC_OFFSET);
+ data[27] = axienet_ior(lp, XAE_UAW0_OFFSET);
+ data[28] = axienet_ior(lp, XAE_UAW1_OFFSET);
+ data[29] = axienet_ior(lp, XAE_FMI_OFFSET);
+ data[30] = axienet_ior(lp, XAE_AF0_OFFSET);
+ data[31] = axienet_ior(lp, XAE_AF1_OFFSET);
+}
+
+/**
+ * axienet_ethtools_get_pauseparam - Get the pause parameter setting for
+ * Tx and Rx paths.
+ * @ndev: Pointer to net_device structure
+ * @epauseparm: Pointer to ethtool_pauseparam structure.
+ *
+ * This implements ethtool command for getting axi ethernet pause frame
+ * setting. Issue "ethtool -a ethX" to execute this function.
+ */
+static void
+axienet_ethtools_get_pauseparam(struct net_device *ndev,
+ struct ethtool_pauseparam *epauseparm)
+{
+ u32 regval;
+ struct axienet_local *lp = netdev_priv(ndev);
+ epauseparm->autoneg = 0;
+ regval = axienet_ior(lp, XAE_FCC_OFFSET);
+ epauseparm->tx_pause = regval & XAE_FCC_FCTX_MASK;
+ epauseparm->rx_pause = regval & XAE_FCC_FCRX_MASK;
+}
+
+/**
+ * axienet_ethtools_set_pauseparam - Set device pause parameter(flow control)
+ * settings.
+ * @ndev: Pointer to net_device structure
+ * @epauseparam: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
+ * function.
+ */
+static int
+axienet_ethtools_set_pauseparam(struct net_device *ndev,
+ struct ethtool_pauseparam *epauseparm)
+{
+ u32 regval = 0;
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ printk(KERN_ERR "%s: Please stop netif before applying "
+ "configruation\n", ndev->name);
+ return -EFAULT;
+ }
+
+ regval = axienet_ior(lp, XAE_FCC_OFFSET);
+ if (epauseparm->tx_pause)
+ regval |= XAE_FCC_FCTX_MASK;
+ else
+ regval &= ~XAE_FCC_FCTX_MASK;
+ if (epauseparm->rx_pause)
+ regval |= XAE_FCC_FCRX_MASK;
+ else
+ regval &= ~XAE_FCC_FCRX_MASK;
+ axienet_iow(lp, XAE_FCC_OFFSET, regval);
+
+ return 0;
+}
+
+/**
+ * axienet_ethtools_get_coalesce - Get DMA interrupt coalescing count.
+ * @ndev: Pointer to net_device structure
+ * @ecoalesce: Pointer to ethtool_coalesce structure
+ *
+ * This implements ethtool command for getting the DMA interrupt coalescing
+ * count on Tx and Rx paths. Issue "ethtool -c ethX" under linux prompt to
+ * execute this function.
+ */
+static int axienet_ethtools_get_coalesce(struct net_device *ndev,
+ struct ethtool_coalesce *ecoalesce)
+{
+ u32 regval = 0;
+ struct axienet_local *lp = netdev_priv(ndev);
+ regval = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
+ ecoalesce->rx_max_coalesced_frames = (regval & XAXIDMA_COALESCE_MASK)
+ >> XAXIDMA_COALESCE_SHIFT;
+ regval = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
+ ecoalesce->tx_max_coalesced_frames = (regval & XAXIDMA_COALESCE_MASK)
+ >> XAXIDMA_COALESCE_SHIFT;
+ return 0;
+}
+
+/**
+ * axienet_ethtools_set_coalesce - Set DMA interrupt coalescing count.
+ * @ndev: Pointer to net_device structure
+ * @ecoalesce: Pointer to ethtool_coalesce structure
+ *
+ * This implements ethtool command for setting the DMA interrupt coalescing
+ * count on Tx and Rx paths. Issue "ethtool -C ethX rx-frames 5" under linux
+ * prompt to execute this function.
+ */
+static int axienet_ethtools_set_coalesce(struct net_device *ndev,
+ struct ethtool_coalesce *ecoalesce)
+{
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ printk(KERN_ERR "%s: Please stop netif before applying "
+ "configruation\n", ndev->name);
+ return -EFAULT;
+ }
+
+ if ((ecoalesce->rx_coalesce_usecs) ||
+ (ecoalesce->rx_coalesce_usecs_irq) ||
+ (ecoalesce->rx_max_coalesced_frames_irq) ||
+ (ecoalesce->tx_coalesce_usecs) ||
+ (ecoalesce->tx_coalesce_usecs_irq) ||
+ (ecoalesce->tx_max_coalesced_frames_irq) ||
+ (ecoalesce->stats_block_coalesce_usecs) ||
+ (ecoalesce->use_adaptive_rx_coalesce) ||
+ (ecoalesce->use_adaptive_tx_coalesce) ||
+ (ecoalesce->pkt_rate_low) ||
+ (ecoalesce->rx_coalesce_usecs_low) ||
+ (ecoalesce->rx_max_coalesced_frames_low) ||
+ (ecoalesce->tx_coalesce_usecs_low) ||
+ (ecoalesce->tx_max_coalesced_frames_low) ||
+ (ecoalesce->pkt_rate_high) ||
+ (ecoalesce->rx_coalesce_usecs_high) ||
+ (ecoalesce->rx_max_coalesced_frames_high) ||
+ (ecoalesce->tx_coalesce_usecs_high) ||
+ (ecoalesce->tx_max_coalesced_frames_high) ||
+ (ecoalesce->rate_sample_interval))
+ return -EOPNOTSUPP;
+ if (ecoalesce->rx_max_coalesced_frames)
+ lp->coalesce_count_rx = ecoalesce->rx_max_coalesced_frames;
+ if (ecoalesce->tx_max_coalesced_frames)
+ lp->coalesce_count_tx = ecoalesce->tx_max_coalesced_frames;
+
+ return 0;
+}
+
+static struct ethtool_ops axienet_ethtool_ops = {
+ .get_settings = axienet_ethtools_get_settings,
+ .set_settings = axienet_ethtools_set_settings,
+ .get_drvinfo = axienet_ethtools_get_drvinfo,
+ .get_regs_len = axienet_ethtools_get_regs_len,
+ .get_regs = axienet_ethtools_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_pauseparam = axienet_ethtools_get_pauseparam,
+ .set_pauseparam = axienet_ethtools_set_pauseparam,
+ .get_coalesce = axienet_ethtools_get_coalesce,
+ .set_coalesce = axienet_ethtools_set_coalesce,
+};
+
+/**
+ * axienet_dma_err_handler - Tasklet handler for Axi DMA Error
+ * @data: Data passed
+ *
+ * Resets the Axi DMA and Axi Ethernet devices, and reconfigures the
+ * Tx/Rx BDs.
+ */
+static void axienet_dma_err_handler(unsigned long data)
+{
+ u32 axienet_status;
+ u32 cr, i;
+ int mdio_mcreg;
+ struct axienet_local *lp = (struct axienet_local *) data;
+ struct net_device *ndev = lp->ndev;
+ struct axidma_bd *cur_p;
+
+ axienet_setoptions(ndev, lp->options &
+ ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
+ mdio_mcreg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
+ axienet_mdio_wait_until_ready(lp);
+ /* 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. */
+ axienet_iow(lp, XAE_MDIO_MC_OFFSET, (mdio_mcreg &
+ ~XAE_MDIO_MC_MDIOEN_MASK));
+
+ __axienet_device_reset(lp, &ndev->dev, XAXIDMA_TX_CR_OFFSET);
+ __axienet_device_reset(lp, &ndev->dev, XAXIDMA_RX_CR_OFFSET);
+
+ axienet_iow(lp, XAE_MDIO_MC_OFFSET, mdio_mcreg);
+ axienet_mdio_wait_until_ready(lp);
+
+ for (i = 0; i < TX_BD_NUM; i++) {
+ cur_p = &lp->tx_bd_v[i];
+ if (cur_p->phys)
+ dma_unmap_single(ndev->dev.parent, cur_p->phys,
+ (cur_p->cntrl &
+ XAXIDMA_BD_CTRL_LENGTH_MASK),
+ DMA_TO_DEVICE);
+ if (cur_p->app4)
+ dev_kfree_skb_irq((struct sk_buff *) cur_p->app4);
+ cur_p->phys = 0;
+ cur_p->cntrl = 0;
+ cur_p->status = 0;
+ cur_p->app0 = 0;
+ cur_p->app1 = 0;
+ cur_p->app2 = 0;
+ cur_p->app3 = 0;
+ cur_p->app4 = 0;
+ cur_p->sw_id_offset = 0;
+ }
+
+ for (i = 0; i < RX_BD_NUM; i++) {
+ cur_p = &lp->rx_bd_v[i];
+ cur_p->status = 0;
+ cur_p->app0 = 0;
+ cur_p->app1 = 0;
+ cur_p->app2 = 0;
+ cur_p->app3 = 0;
+ cur_p->app4 = 0;
+ }
+
+ lp->tx_bd_ci = 0;
+ lp->tx_bd_tail = 0;
+ lp->rx_bd_ci = 0;
+
+ /* Start updating the Rx channel control register */
+ cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
+ /* Update the interrupt coalesce count */
+ cr = ((cr & ~XAXIDMA_COALESCE_MASK) |
+ (XAXIDMA_DFT_RX_THRESHOLD << XAXIDMA_COALESCE_SHIFT));
+ /* Update the delay timer count */
+ cr = ((cr & ~XAXIDMA_DELAY_MASK) |
+ (XAXIDMA_DFT_RX_WAITBOUND << XAXIDMA_DELAY_SHIFT));
+ /* Enable coalesce, delay timer and error interrupts */
+ cr |= XAXIDMA_IRQ_ALL_MASK;
+ /* Finally write to the Rx channel control register */
+ axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
+
+ /* Start updating the Tx channel control register */
+ cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
+ /* Update the interrupt coalesce count */
+ cr = (((cr & ~XAXIDMA_COALESCE_MASK)) |
+ (XAXIDMA_DFT_TX_THRESHOLD << XAXIDMA_COALESCE_SHIFT));
+ /* Update the delay timer count */
+ cr = (((cr & ~XAXIDMA_DELAY_MASK)) |
+ (XAXIDMA_DFT_TX_WAITBOUND << XAXIDMA_DELAY_SHIFT));
+ /* Enable coalesce, delay timer and error interrupts */
+ cr |= XAXIDMA_IRQ_ALL_MASK;
+ /* Finally write to the Tx channel control register */
+ 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.*/
+ 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,
+ cr | XAXIDMA_CR_RUNSTOP_MASK);
+ axienet_dma_out32(lp, XAXIDMA_RX_TDESC_OFFSET, lp->rx_bd_p +
+ (sizeof(*lp->rx_bd_v) * (RX_BD_NUM - 1)));
+
+ /* 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 */
+ 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,
+ cr | XAXIDMA_CR_RUNSTOP_MASK);
+
+ axienet_status = axienet_ior(lp, XAE_RCW1_OFFSET);
+ axienet_status &= ~XAE_RCW1_RX_MASK;
+ axienet_iow(lp, XAE_RCW1_OFFSET, axienet_status);
+
+ axienet_status = axienet_ior(lp, XAE_IP_OFFSET);
+ if (axienet_status & XAE_INT_RXRJECT_MASK)
+ axienet_iow(lp, XAE_IS_OFFSET, XAE_INT_RXRJECT_MASK);
+ axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
+
+ /* Sync default options with HW but leave receiver and
+ * transmitter disabled.*/
+ axienet_setoptions(ndev, lp->options &
+ ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
+ axienet_set_mac_address(ndev, NULL);
+ axienet_set_multicast_list(ndev);
+ axienet_setoptions(ndev, lp->options);
+}
+
+/**
+ * axienet_of_probe - Axi Ethernet probe function.
+ * @op: Pointer to platform device structure.
+ * @match: Pointer to device id structure
+ *
+ * returns: 0, on success
+ * Non-zero error value on failure.
+ *
+ * This is the probe routine for Axi Ethernet driver. This is called before
+ * any other driver routines are invoked. It allocates and sets up the Ethernet
+ * device. Parses through device tree and populates fields of
+ * axienet_local. It registers the Ethernet device.
+ */
+static int __devinit axienet_of_probe(struct platform_device *op)
+{
+ __be32 *p;
+ int size, ret = 0;
+ struct device_node *np;
+ struct axienet_local *lp;
+ struct net_device *ndev;
+ const void *addr;
+
+ ndev = alloc_etherdev(sizeof(*lp));
+ if (!ndev)
+ return -ENOMEM;
+
+ ether_setup(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;
+ ndev->netdev_ops = &axienet_netdev_ops;
+ ndev->ethtool_ops = &axienet_ethtool_ops;
+
+ lp = netdev_priv(ndev);
+ lp->ndev = ndev;
+ lp->dev = &op->dev;
+ lp->options = XAE_OPTION_DEFAULTS;
+ /* Map device registers */
+ lp->regs = of_iomap(op->dev.of_node, 0);
+ if (!lp->regs) {
+ dev_err(&op->dev, "could not map Axi Ethernet regs.\n");
+ goto nodev;
+ }
+ /* 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)) {
+ 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;
+ 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;
+ 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)) {
+ case 1:
+ lp->csum_offload_on_rx_path =
+ XAE_FEATURE_PARTIAL_RX_CSUM;
+ lp->features |= XAE_FEATURE_PARTIAL_RX_CSUM;
+ break;
+ case 2:
+ lp->csum_offload_on_rx_path =
+ XAE_FEATURE_FULL_RX_CSUM;
+ lp->features |= XAE_FEATURE_FULL_RX_CSUM;
+ break;
+ default:
+ lp->csum_offload_on_rx_path = XAE_NO_CSUM_OFFLOAD;
+ }
+ }
+ /* 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);
+
+ /* 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;
+ }
+ 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);
+ }
+ 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 == NO_IRQ) || (lp->tx_irq == NO_IRQ)) {
+ dev_err(&op->dev, "could not determine irqs\n");
+ ret = -ENOMEM;
+ goto err_iounmap_2;
+ }
+
+ /* 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;
+ }
+ axienet_set_mac_address(ndev, (void *) 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");
+
+ ret = register_netdev(lp->ndev);
+ if (ret) {
+ dev_err(lp->dev, "register_netdev() error (%i)\n", ret);
+ goto err_iounmap_2;
+ }
+
+ tasklet_init(&lp->dma_err_tasklet, axienet_dma_err_handler,
+ (unsigned long) lp);
+ tasklet_disable(&lp->dma_err_tasklet);
+
+ return 0;
+
+err_iounmap_2:
+ if (lp->dma_regs)
+ iounmap(lp->dma_regs);
+err_iounmap:
+ iounmap(lp->regs);
+nodev:
+ free_netdev(ndev);
+ ndev = NULL;
+ return ret;
+}
+
+static int __devexit axienet_of_remove(struct platform_device *op)
+{
+ struct net_device *ndev = dev_get_drvdata(&op->dev);
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ axienet_mdio_teardown(lp);
+ unregister_netdev(ndev);
+
+ 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->dma_regs)
+ iounmap(lp->dma_regs);
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static struct platform_driver axienet_of_driver = {
+ .probe = axienet_of_probe,
+ .remove = __devexit_p(axienet_of_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "xilinx_axienet",
+ .of_match_table = axienet_of_match,
+ },
+};
+
+module_platform_driver(axienet_of_driver);
+
+MODULE_DESCRIPTION("Xilinx Axi Ethernet driver");
+MODULE_AUTHOR("Xilinx");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
new file mode 100644
index 000000000000..d70b6e79f6c0
--- /dev/null
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
@@ -0,0 +1,238 @@
+/*
+ * MDIO bus driver for the Xilinx Axi Ethernet device
+ *
+ * Copyright (c) 2009 Secret Lab Technologies, Ltd.
+ * Copyright (c) 2010 Xilinx, Inc. All rights reserved.
+ * Copyright (c) 2012 Daniel Borkmann, <daniel.borkmann@tik.ee.ethz.ch>
+ * Copyright (c) 2012 Ariane Keller, <ariane.keller@tik.ee.ethz.ch>
+ */
+
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/jiffies.h>
+
+#include "xilinx_axienet.h"
+
+#define MAX_MDIO_FREQ 2500000 /* 2.5 MHz */
+#define DEFAULT_CLOCK_DIVISOR XAE_MDIO_DIV_DFT
+
+/* Wait till MDIO interface is ready to accept a new transaction.*/
+int axienet_mdio_wait_until_ready(struct axienet_local *lp)
+{
+ long end = jiffies + 2;
+ while (!(axienet_ior(lp, XAE_MDIO_MCR_OFFSET) &
+ XAE_MDIO_MCR_READY_MASK)) {
+ if (end - jiffies <= 0) {
+ WARN_ON(1);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+ return 0;
+}
+
+/**
+ * axienet_mdio_read - MDIO interface read function
+ * @bus: Pointer to mii bus structure
+ * @phy_id: Address of the PHY device
+ * @reg: PHY register to read
+ *
+ * returns: The register contents on success, -ETIMEDOUT on a timeout
+ *
+ * Reads the contents of the requested register from the requested PHY
+ * address by first writing the details into MCR register. After a while
+ * the register MRD is read to obtain the PHY register content.
+ */
+static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
+{
+ u32 rc;
+ int ret;
+ struct axienet_local *lp = bus->priv;
+
+ ret = axienet_mdio_wait_until_ready(lp);
+ if (ret < 0)
+ return ret;
+
+ axienet_iow(lp, XAE_MDIO_MCR_OFFSET,
+ (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &
+ XAE_MDIO_MCR_PHYAD_MASK) |
+ ((reg << XAE_MDIO_MCR_REGAD_SHIFT) &
+ XAE_MDIO_MCR_REGAD_MASK) |
+ XAE_MDIO_MCR_INITIATE_MASK |
+ XAE_MDIO_MCR_OP_READ_MASK));
+
+ ret = axienet_mdio_wait_until_ready(lp);
+ if (ret < 0)
+ return ret;
+
+ rc = axienet_ior(lp, XAE_MDIO_MRD_OFFSET) & 0x0000FFFF;
+
+ dev_dbg(lp->dev, "axienet_mdio_read(phy_id=%i, reg=%x) == %x\n",
+ phy_id, reg, rc);
+
+ return rc;
+}
+
+/**
+ * axienet_mdio_write - MDIO interface write function
+ * @bus: Pointer to mii bus structure
+ * @phy_id: Address of the PHY device
+ * @reg: PHY register to write to
+ * @val: Value to be written into the register
+ *
+ * returns: 0 on success, -ETIMEDOUT on a timeout
+ *
+ * Writes the value to the requested register by first writing the value
+ * into MWD register. The the MCR register is then appropriately setup
+ * to finish the write operation.
+ */
+static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
+ u16 val)
+{
+ int ret;
+ struct axienet_local *lp = bus->priv;
+
+ dev_dbg(lp->dev, "axienet_mdio_write(phy_id=%i, reg=%x, val=%x)\n",
+ phy_id, reg, val);
+
+ ret = axienet_mdio_wait_until_ready(lp);
+ if (ret < 0)
+ return ret;
+
+ axienet_iow(lp, XAE_MDIO_MWD_OFFSET, (u32) val);
+ axienet_iow(lp, XAE_MDIO_MCR_OFFSET,
+ (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &
+ XAE_MDIO_MCR_PHYAD_MASK) |
+ ((reg << XAE_MDIO_MCR_REGAD_SHIFT) &
+ XAE_MDIO_MCR_REGAD_MASK) |
+ XAE_MDIO_MCR_INITIATE_MASK |
+ XAE_MDIO_MCR_OP_WRITE_MASK));
+
+ ret = axienet_mdio_wait_until_ready(lp);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+/**
+ * axienet_mdio_setup - MDIO setup function
+ * @lp: Pointer to axienet local data structure.
+ * @np: Pointer to device node
+ *
+ * returns: 0 on success, -ETIMEDOUT on a timeout, -ENOMEM when
+ * mdiobus_alloc (to allocate memory for mii bus structure) fails.
+ *
+ * Sets up the MDIO interface by initializing the MDIO clock and enabling the
+ * MDIO interface in hardware. Register the MDIO interface.
+ **/
+int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
+{
+ int ret;
+ u32 clk_div, host_clock;
+ u32 *property_p;
+ struct mii_bus *bus;
+ struct resource res;
+ struct device_node *np1;
+
+ /* clk_div can be calculated by deriving it from the equation:
+ * fMDIO = fHOST / ((1 + clk_div) * 2)
+ *
+ * Where fMDIO <= 2500000, so we get:
+ * fHOST / ((1 + clk_div) * 2) <= 2500000
+ *
+ * Then we get:
+ * 1 / ((1 + clk_div) * 2) <= (2500000 / fHOST)
+ *
+ * Then we get:
+ * 1 / (1 + clk_div) <= ((2500000 * 2) / fHOST)
+ *
+ * Then we get:
+ * 1 / (1 + clk_div) <= (5000000 / fHOST)
+ *
+ * So:
+ * (1 + clk_div) >= (fHOST / 5000000)
+ *
+ * And finally:
+ * clk_div >= (fHOST / 5000000) - 1
+ *
+ * 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);
+ clk_div = DEFAULT_CLOCK_DIVISOR;
+ goto issue;
+ }
+
+ 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));
+
+ ret = axienet_mdio_wait_until_ready(lp);
+ if (ret < 0)
+ return ret;
+
+ bus = mdiobus_alloc();
+ if (!bus)
+ return -ENOMEM;
+
+ np1 = of_get_parent(lp->phy_node);
+ of_address_to_resource(np1, 0, &res);
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx",
+ (unsigned long long) res.start);
+
+ bus->priv = lp;
+ bus->name = "Xilinx Axi Ethernet MDIO";
+ bus->read = axienet_mdio_read;
+ bus->write = axienet_mdio_write;
+ bus->parent = lp->dev;
+ bus->irq = lp->mdio_irqs; /* preallocated IRQ table */
+ lp->mii_bus = bus;
+
+ ret = of_mdiobus_register(bus, np1);
+ if (ret) {
+ mdiobus_free(bus);
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * axienet_mdio_teardown - MDIO remove function
+ * @lp: Pointer to axienet local data structure.
+ *
+ * Unregisters the MDIO and frees any associate memory for mii bus.
+ */
+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 79013e5731a5..77cfe5110318 100644
--- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c
+++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
@@ -613,7 +613,7 @@ static void xemaclite_rx_handler(struct net_device *dev)
u32 len;
len = ETH_FRAME_LEN + ETH_FCS_LEN;
- skb = dev_alloc_skb(len + ALIGNMENT);
+ skb = netdev_alloc_skb(dev, len + ALIGNMENT);
if (!skb) {
/* Couldn't get memory. */
dev->stats.rx_dropped++;
@@ -1136,10 +1136,8 @@ static int __devinit xemaclite_of_probe(struct platform_device *ofdev)
/* Create an ethernet device instance */
ndev = alloc_etherdev(sizeof(struct net_local));
- if (!ndev) {
- dev_err(dev, "Could not allocate network device\n");
+ if (!ndev)
return -ENOMEM;
- }
dev_set_drvdata(dev, ndev);
SET_NETDEV_DEV(ndev, &ofdev->dev);