From d8a5b215f3047cc9f36f94336e09ea20fdaa5879 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 18 Jan 2012 11:45:36 +0100 Subject: [PATCH] microblaze: added kernel patch for Xilinx AXI Ethernet --- microblaze/xilinx_axi.patch | 2499 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2499 insertions(+) create mode 100644 microblaze/xilinx_axi.patch diff --git a/microblaze/xilinx_axi.patch b/microblaze/xilinx_axi.patch new file mode 100644 index 0000000..3c8147f --- /dev/null +++ b/microblaze/xilinx_axi.patch @@ -0,0 +1,2499 @@ +Against net-next, rev 356b95424cfb456e14a59eaa579422ce014c424b + +Signed-off-by: Ariane Keller +Signed-off-by: Daniel Borkmann + +diff --git a/MAINTAINERS b/MAINTAINERS +index 4babed7..4d0b9f7 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -7459,6 +7459,13 @@ S: Supported + F: Documentation/filesystems/xfs.txt + F: fs/xfs/ + ++XILINX AXI ETHERNET DRIVER ++M: Ariane Keller ++M: Daniel Borkmann ++W: http://www.gnumaniacs.org/ ++S: Maintained ++F: drivers/net/ethernet/xilinx/xilinx_axienet* ++ + XILINX SYSTEMACE DRIVER + M: Grant Likely + W: http://www.secretlab.ca/ +diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig +index d5a8260..634e705 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 5feac73..214205e 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/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h +new file mode 100644 +index 0000000..cc83af0 +--- /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, ++ * Copyright (c) 2012 Ariane Keller, ++ */ ++ ++#ifndef XILINX_AXIENET_H ++#define XILINX_AXIENET_H ++ ++#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_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 0000000..7fe9a1b +--- /dev/null ++++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +@@ -0,0 +1,1683 @@ ++/* ++ * Xilinx Axi Ethernet device driver ++ * ++ * Copyright (c) 2008 Nissin Systems Co., Ltd., Yoshio Kashiwagi ++ * Copyright (c) 2005-2008 DLA Systems, David H. Lynch Jr. ++ * Copyright (c) 2008-2009 Secret Lab Technologies Ltd. ++ * Copyright (c) 2010 Xilinx, Inc. All rights reserved. ++ * Copyright (c) 2012 Daniel Borkmann, ++ * Copyright (c) 2012 Ariane Keller, ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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) { ++ dev_err(&op->dev, "could not allocate device.\n"); ++ 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, ++ }, ++}; ++ ++static int __init axienet_init(void) ++{ ++ return platform_driver_register(&axienet_of_driver); ++} ++ ++static void __exit axienet_exit(void) ++{ ++ platform_driver_unregister(&axienet_of_driver); ++} ++ ++module_init(axienet_init); ++module_exit(axienet_exit); ++ ++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 0000000..d70b6e7 +--- /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, ++ * Copyright (c) 2012 Ariane Keller, ++ */ ++ ++#include ++#include ++#include ++ ++#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; ++} -- 2.11.4.GIT