@@ -27,18 +27,25 @@ config XILINX_EMACLITE
config XILINX_AXI_EMAC
tristate "Xilinx 10/100/1000 AXI Ethernet support"
- depends on MICROBLAZE
+ depends on (MICROBLAZE || ARCH_ZYNQ)
select PHYLIB
---help---
This driver supports the 10/100/1000 Ethernet from Xilinx for the
AXI bus interface used in Xilinx Virtex FPGAs.
-config XILINX_LL_TEMAC
- tristate "Xilinx LL TEMAC (LocalLink Tri-mode Ethernet MAC) driver"
- depends on (PPC || MICROBLAZE)
+config XILINX_PS_EMAC
+ tristate "Xilinx PS tri-speed EMAC support"
+ depends on ARCH_ZYNQ
select PHYLIB
---help---
- This driver supports the Xilinx 10/100/1000 LocalLink TEMAC
- core used in Xilinx Spartan and Virtex FPGAs
+ This driver supports tri-speed EMAC.
+
+config XILINX_PS_EMAC_HWTSTAMP
+ bool "Generate hardware packet timestamps"
+ depends on XILINX_PS_EMAC
+ default n
+ ---help---
+ Generate hardare packet timestamps. This is to facilitate IEE 1588.
+
endif # NET_VENDOR_XILINX
@@ -5,5 +5,6 @@
ll_temac-objs := ll_temac_main.o ll_temac_mdio.o
obj-$(CONFIG_XILINX_LL_TEMAC) += ll_temac.o
obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o
+obj-$(CONFIG_XILINX_PS_EMAC) += xilinx_emacps.o
xilinx_emac-objs := xilinx_axienet_main.o xilinx_axienet_mdio.o
obj-$(CONFIG_XILINX_AXI_EMAC) += xilinx_emac.o
@@ -36,7 +36,6 @@
#include <linux/netdevice.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/of_irq.h>
#include <linux/of_mdio.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
@@ -244,15 +243,15 @@ static int temac_dma_bd_init(struct net_device *ndev)
/* allocate the tx and rx ring buffer descriptors. */
/* returns a virtual address and a physical address. */
- lp->tx_bd_v = dma_zalloc_coherent(ndev->dev.parent,
- sizeof(*lp->tx_bd_v) * TX_BD_NUM,
- &lp->tx_bd_p, GFP_KERNEL);
+ lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent,
+ sizeof(*lp->tx_bd_v) * TX_BD_NUM,
+ &lp->tx_bd_p, GFP_KERNEL | __GFP_ZERO);
if (!lp->tx_bd_v)
goto out;
- lp->rx_bd_v = dma_zalloc_coherent(ndev->dev.parent,
- sizeof(*lp->rx_bd_v) * RX_BD_NUM,
- &lp->rx_bd_p, GFP_KERNEL);
+ lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent,
+ sizeof(*lp->rx_bd_v) * RX_BD_NUM,
+ &lp->rx_bd_p, GFP_KERNEL | __GFP_ZERO);
if (!lp->rx_bd_v)
goto out;
@@ -298,12 +297,6 @@ static int temac_dma_bd_init(struct net_device *ndev)
lp->rx_bd_p + (sizeof(*lp->rx_bd_v) * (RX_BD_NUM - 1)));
lp->dma_out(lp, TX_CURDESC_PTR, lp->tx_bd_p);
- /* Init descriptor indexes */
- lp->tx_bd_ci = 0;
- lp->tx_bd_next = 0;
- lp->tx_bd_tail = 0;
- lp->rx_bd_ci = 0;
-
return 0;
out:
@@ -685,15 +678,12 @@ static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_frag_t *frag;
num_frag = skb_shinfo(skb)->nr_frags;
- frag = &skb_shinfo(skb)->frags[0];
start_p = lp->tx_bd_p + sizeof(*lp->tx_bd_v) * lp->tx_bd_tail;
cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
if (temac_check_tx_bd_space(lp, num_frag)) {
- if (!netif_queue_stopped(ndev)) {
+ if (!netif_queue_stopped(ndev))
netif_stop_queue(ndev);
- return NETDEV_TX_BUSY;
- }
return NETDEV_TX_BUSY;
}
@@ -709,11 +699,12 @@ static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
cur_p->app0 |= STS_CTRL_APP0_SOP;
cur_p->len = skb_headlen(skb);
- cur_p->phys = dma_map_single(ndev->dev.parent, skb->data, skb->len,
- DMA_TO_DEVICE);
+ cur_p->phys = dma_map_single(ndev->dev.parent, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
cur_p->app4 = (unsigned long)skb;
for (ii = 0; ii < num_frag; ii++) {
+ frag = &skb_shinfo(skb)->frags[ii];
lp->tx_bd_tail++;
if (lp->tx_bd_tail >= TX_BD_NUM)
lp->tx_bd_tail = 0;
@@ -724,7 +715,6 @@ static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_frag_size(frag), DMA_TO_DEVICE);
cur_p->len = skb_frag_size(frag);
cur_p->app0 = 0;
- frag++;
}
cur_p->app0 |= STS_CTRL_APP0_EOP;
@@ -1014,7 +1004,7 @@ static int temac_of_probe(struct platform_device *op)
return -ENOMEM;
ether_setup(ndev);
- platform_set_drvdata(op, ndev);
+ dev_set_drvdata(&op->dev, ndev);
SET_NETDEV_DEV(ndev, &op->dev);
ndev->flags &= ~IFF_MULTICAST; /* clear multicast */
ndev->features = NETIF_F_SG | NETIF_F_FRAGLIST;
@@ -1052,12 +1042,14 @@ static int temac_of_probe(struct platform_device *op)
/* Setup checksum offload, but default to off if not specified */
lp->temac_features = 0;
p = (__be32 *)of_get_property(op->dev.of_node, "xlnx,txcsum", NULL);
+ dev_info(&op->dev, "TX_CSUM %d\n", be32_to_cpup(p));
if (p && be32_to_cpu(*p)) {
lp->temac_features |= TEMAC_FEATURE_TX_CSUM;
/* Can checksum TCP/UDP over IPv4. */
ndev->features |= NETIF_F_IP_CSUM;
}
p = (__be32 *)of_get_property(op->dev.of_node, "xlnx,rxcsum", NULL);
+ dev_info(&op->dev, "RX_CSUM %d\n", be32_to_cpup(p));
if (p && be32_to_cpu(*p))
lp->temac_features |= TEMAC_FEATURE_RX_CSUM;
@@ -1105,14 +1097,15 @@ static int temac_of_probe(struct platform_device *op)
}
temac_init_mac_address(ndev, (void *)addr);
- rc = temac_mdio_setup(lp, op->dev.of_node);
- if (rc)
- dev_warn(&op->dev, "error registering MDIO bus\n");
-
lp->phy_node = of_parse_phandle(op->dev.of_node, "phy-handle", 0);
- if (lp->phy_node)
+ if (lp->phy_node) {
dev_dbg(lp->dev, "using PHY node %s (%p)\n", np->full_name, np);
+ rc = temac_mdio_setup(lp, op->dev.of_node);
+ if (rc)
+ dev_warn(&op->dev, "error registering MDIO bus\n");
+ }
+
/* Add the device attributes */
rc = sysfs_create_group(&lp->dev->kobj, &temac_attr_group);
if (rc) {
@@ -1143,7 +1136,7 @@ static int temac_of_probe(struct platform_device *op)
static int temac_of_remove(struct platform_device *op)
{
- struct net_device *ndev = platform_get_drvdata(op);
+ struct net_device *ndev = dev_get_drvdata(&op->dev);
struct temac_local *lp = netdev_priv(ndev);
temac_mdio_teardown(lp);
@@ -1152,6 +1145,7 @@ static int temac_of_remove(struct platform_device *op)
if (lp->phy_node)
of_node_put(lp->phy_node);
lp->phy_node = NULL;
+ dev_set_drvdata(&op->dev, NULL);
iounmap(lp->regs);
if (lp->sdma_regs)
iounmap(lp->sdma_regs);
@@ -63,6 +63,7 @@ int temac_mdio_setup(struct temac_local *lp, struct device_node *np)
int clk_div;
int rc, size;
struct resource res;
+ struct device_node *np1 = of_get_parent(lp->phy_node);
/* Calculate a reasonable divisor for the clock rate */
clk_div = 0x3f; /* worst-case default setting */
@@ -85,7 +86,7 @@ int temac_mdio_setup(struct temac_local *lp, struct device_node *np)
if (!bus)
return -ENOMEM;
- of_address_to_resource(np, 0, &res);
+ of_address_to_resource(np1, 0, &res);
snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx",
(unsigned long long)res.start);
bus->priv = lp;
@@ -97,7 +98,7 @@ int temac_mdio_setup(struct temac_local *lp, struct device_node *np)
lp->mii_bus = bus;
- rc = of_mdiobus_register(bus, np);
+ rc = of_mdiobus_register(bus, np1);
if (rc)
goto err_register;
@@ -9,18 +9,19 @@
#define XILINX_AXIENET_H
#include <linux/netdevice.h>
+#include <linux/of_irq.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
+#include <linux/if_vlan.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_VLAN_FRAME_SIZE (XAE_MTU + VLAN_ETH_HLEN + XAE_TRL_SIZE)
#define XAE_MAX_JUMBO_FRAME_SIZE (XAE_JUMBO_MTU + XAE_HDR_SIZE + XAE_TRL_SIZE)
/* Configuration options */
@@ -66,20 +67,20 @@
/* 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_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_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_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_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 */
@@ -93,8 +94,8 @@
#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_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 */
@@ -130,7 +131,7 @@
#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_STS_ALL_MASK 0xFC000000 /* All status bits */
#define XAXIDMA_BD_MINIMUM_ALIGNMENT 0x40
@@ -158,7 +159,7 @@
#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_MIS_OFFSET 0x00000600 /* MII Management Int. Status */
#define XAE_MDIO_MIP_OFFSET 0x00000620 /* MII Mgmt Interrupt Pending
* register offset */
#define XAE_MDIO_MIE_OFFSET 0x00000640 /* MII Management Interrupt Enable
@@ -180,16 +181,16 @@
* 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_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_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_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*/
@@ -273,22 +274,22 @@
#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_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_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 */
+#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_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_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_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 */
@@ -312,13 +313,13 @@
#define XAE_MDIO_DIV_DFT 29 /* Default MDIO clock divisor */
-/* Defines for different options for C_PHY_TYPE parameter in Axi Ethernet IP */
+/* Defines 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_PHY_TYPE_1000BASE_X 5
#define XAE_MULTICAST_CAM_TABLE_NUM 4 /* Total number of entries in the
* hardware multicast table. */
@@ -337,6 +338,14 @@
#define DELAY_OF_ONE_MILLISEC 1000
+/* Read/Write access to the registers */
+#ifndef out_be32
+#ifdef CONFIG_ARCH_ZYNQ
+#define in_be32(offset) __raw_readl(offset)
+#define out_be32(offset, val) __raw_writel(val, offset)
+#endif
+#endif
+
/**
* struct axidma_bd - Axi Dma buffer descriptor layout
* @next: MM2S/S2MM Next Descriptor Pointer
@@ -408,8 +417,9 @@ struct axidma_bd {
* 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.
+ * @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;
@@ -434,7 +444,7 @@ struct axienet_local {
u32 temac_type;
u32 phy_type;
- u32 options; /* Current options word */
+ u32 options; /* Current options word */
u32 last_link;
u32 features;
@@ -448,7 +458,7 @@ struct axienet_local {
u32 rx_bd_ci;
u32 max_frm_size;
- u32 jumbo_support;
+ u32 rxmem;
int csum_offload_on_tx_path;
int csum_offload_on_rx_path;
@@ -201,15 +201,17 @@ static int axienet_dma_bd_init(struct net_device *ndev)
/*
* Allocate the Tx and Rx buffer descriptors.
*/
- lp->tx_bd_v = dma_zalloc_coherent(ndev->dev.parent,
- sizeof(*lp->tx_bd_v) * TX_BD_NUM,
- &lp->tx_bd_p, GFP_KERNEL);
+ lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent,
+ sizeof(*lp->tx_bd_v) * TX_BD_NUM,
+ &lp->tx_bd_p,
+ GFP_KERNEL | __GFP_ZERO);
if (!lp->tx_bd_v)
goto out;
- lp->rx_bd_v = dma_zalloc_coherent(ndev->dev.parent,
- sizeof(*lp->rx_bd_v) * RX_BD_NUM,
- &lp->rx_bd_p, GFP_KERNEL);
+ lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent,
+ sizeof(*lp->rx_bd_v) * RX_BD_NUM,
+ &lp->rx_bd_p,
+ GFP_KERNEL | __GFP_ZERO);
if (!lp->rx_bd_v)
goto out;
@@ -263,7 +265,8 @@ static int axienet_dma_bd_init(struct net_device *ndev)
axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr);
/* Populate the tail pointer and bring the Rx Axi DMA engine out of
- * halted state. This will make the Rx side ready for reception.*/
+ * halted state. This will make the Rx side ready for reception.
+ */
axienet_dma_out32(lp, XAXIDMA_RX_CDESC_OFFSET, lp->rx_bd_p);
cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET,
@@ -273,7 +276,8 @@ static int axienet_dma_bd_init(struct net_device *ndev)
/* Write to the RS (Run-stop) bit in the Tx channel control register.
* Tx channel is now ready to run. But only after we write to the
- * tail pointer register that the Tx channel will start transmitting */
+ * tail pointer register that the Tx channel will start transmitting.
+ */
axienet_dma_out32(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p);
cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET,
@@ -354,7 +358,8 @@ static void axienet_set_multicast_list(struct net_device *ndev)
netdev_mc_count(ndev) > XAE_MULTICAST_CAM_TABLE_NUM) {
/* We must make the kernel realize we had to move into
* promiscuous mode. If it was a promiscuous mode request
- * the flag is already set. If not we set it. */
+ * the flag is already set. If not we set it.
+ */
ndev->flags |= IFF_PROMISC;
reg = axienet_ior(lp, XAE_FMI_OFFSET);
reg |= XAE_FMI_PM_MASK;
@@ -438,14 +443,15 @@ static void __axienet_device_reset(struct axienet_local *lp,
/* Reset Axi DMA. This would reset Axi Ethernet core as well. The reset
* process of Axi DMA takes a while to complete as all pending
* commands/transfers will be flushed or completed during this
- * reset process. */
+ * reset process.
+ */
axienet_dma_out32(lp, offset, XAXIDMA_CR_RESET_MASK);
timeout = DELAY_OF_ONE_MILLISEC;
while (axienet_dma_in32(lp, offset) & XAXIDMA_CR_RESET_MASK) {
udelay(1);
if (--timeout == 0) {
- dev_err(dev, "axienet_device_reset DMA "
- "reset timeout!\n");
+ dev_err(dev,
+ "axienet_device_reset DMA reset timeout!\n");
break;
}
}
@@ -471,18 +477,21 @@ static void axienet_device_reset(struct net_device *ndev)
__axienet_device_reset(lp, &ndev->dev, XAXIDMA_RX_CR_OFFSET);
lp->max_frm_size = XAE_MAX_VLAN_FRAME_SIZE;
+ lp->options |= XAE_OPTION_VLAN;
lp->options &= (~XAE_OPTION_JUMBO);
if ((ndev->mtu > XAE_MTU) &&
- (ndev->mtu <= XAE_JUMBO_MTU) &&
- (lp->jumbo_support)) {
- lp->max_frm_size = ndev->mtu + XAE_HDR_VLAN_SIZE +
- XAE_TRL_SIZE;
- lp->options |= XAE_OPTION_JUMBO;
+ (ndev->mtu <= XAE_JUMBO_MTU)) {
+ lp->max_frm_size = ndev->mtu + VLAN_ETH_HLEN +
+ XAE_TRL_SIZE;
+
+ if (lp->max_frm_size <= lp->rxmem)
+ lp->options |= XAE_OPTION_JUMBO;
}
if (axienet_dma_bd_init(ndev)) {
- dev_err(&ndev->dev, "axienet_device_reset descriptor "
+ dev_err(&ndev->dev,
+ "axienet_device_reset descriptor "
"allocation failed\n");
}
@@ -497,7 +506,8 @@ static void axienet_device_reset(struct net_device *ndev)
axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
/* Sync default options with HW but leave receiver and
- * transmitter disabled.*/
+ * transmitter disabled.
+ */
axienet_setoptions(ndev, lp->options &
~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
axienet_set_mac_address(ndev, NULL);
@@ -549,7 +559,8 @@ static void axienet_adjust_link(struct net_device *ndev)
emmc_reg |= XAE_EMMC_LINKSPD_10;
break;
default:
- dev_err(&ndev->dev, "Speed other than 10, 100 "
+ dev_err(&ndev->dev,
+ "Speed other than 10, 100 "
"or 1Gbps is not supported\n");
break;
}
@@ -558,8 +569,8 @@ static void axienet_adjust_link(struct net_device *ndev)
lp->last_link = link_state;
phy_print_status(phy);
} else {
- dev_err(&ndev->dev, "Error setting Axi Ethernet "
- "mac speed\n");
+ dev_err(&ndev->dev,
+ "Error setting Axi Ethernet mac speed\n");
}
}
}
@@ -601,7 +612,8 @@ static void axienet_start_xmit_done(struct net_device *ndev)
size += status & XAXIDMA_BD_STS_ACTUAL_LEN_MASK;
packets++;
- lp->tx_bd_ci = ++lp->tx_bd_ci % TX_BD_NUM;
+ ++lp->tx_bd_ci;
+ lp->tx_bd_ci %= TX_BD_NUM;
cur_p = &lp->tx_bd_v[lp->tx_bd_ci];
status = cur_p->status;
}
@@ -687,7 +699,8 @@ static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_headlen(skb), DMA_TO_DEVICE);
for (ii = 0; ii < num_frag; ii++) {
- lp->tx_bd_tail = ++lp->tx_bd_tail % TX_BD_NUM;
+ ++lp->tx_bd_tail;
+ lp->tx_bd_tail %= TX_BD_NUM;
cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
frag = &skb_shinfo(skb)->frags[ii];
cur_p->phys = dma_map_single(ndev->dev.parent,
@@ -701,9 +714,12 @@ static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
cur_p->app4 = (unsigned long)skb;
tail_p = lp->tx_bd_p + sizeof(*lp->tx_bd_v) * lp->tx_bd_tail;
+ wmb();
+
/* Start the transfer */
axienet_dma_out32(lp, XAXIDMA_TX_TDESC_OFFSET, tail_p);
- lp->tx_bd_tail = ++lp->tx_bd_tail % TX_BD_NUM;
+ ++lp->tx_bd_tail;
+ lp->tx_bd_tail %= TX_BD_NUM;
return NETDEV_TX_OK;
}
@@ -723,15 +739,16 @@ static void axienet_recv(struct net_device *ndev)
u32 csumstatus;
u32 size = 0;
u32 packets = 0;
- dma_addr_t tail_p;
+ dma_addr_t tail_p = 0;
struct axienet_local *lp = netdev_priv(ndev);
struct sk_buff *skb, *new_skb;
struct axidma_bd *cur_p;
- tail_p = lp->rx_bd_p + sizeof(*lp->rx_bd_v) * lp->rx_bd_ci;
+ rmb();
cur_p = &lp->rx_bd_v[lp->rx_bd_ci];
while ((cur_p->status & XAXIDMA_BD_STS_COMPLETE_MASK)) {
+ tail_p = lp->rx_bd_p + sizeof(*lp->rx_bd_v) * lp->rx_bd_ci;
skb = (struct sk_buff *) (cur_p->sw_id_offset);
length = cur_p->app4 & 0x0000FFFF;
@@ -775,14 +792,16 @@ static void axienet_recv(struct net_device *ndev)
cur_p->status = 0;
cur_p->sw_id_offset = (u32) new_skb;
- lp->rx_bd_ci = ++lp->rx_bd_ci % RX_BD_NUM;
+ ++lp->rx_bd_ci;
+ lp->rx_bd_ci %= RX_BD_NUM;
cur_p = &lp->rx_bd_v[lp->rx_bd_ci];
}
ndev->stats.rx_packets += packets;
ndev->stats.rx_bytes += size;
- axienet_dma_out32(lp, XAXIDMA_RX_TDESC_OFFSET, tail_p);
+ if (tail_p)
+ axienet_dma_out32(lp, XAXIDMA_RX_TDESC_OFFSET, tail_p);
}
/**
@@ -804,6 +823,7 @@ static irqreturn_t axienet_tx_irq(int irq, void *_ndev)
status = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET);
if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) {
+ axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status);
axienet_start_xmit_done(lp->ndev);
goto out;
}
@@ -827,9 +847,9 @@ static irqreturn_t axienet_tx_irq(int irq, void *_ndev)
axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
tasklet_schedule(&lp->dma_err_tasklet);
+ axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status);
}
out:
- axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status);
return IRQ_HANDLED;
}
@@ -852,6 +872,7 @@ static irqreturn_t axienet_rx_irq(int irq, void *_ndev)
status = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET);
if (status & (XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK)) {
+ axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status);
axienet_recv(lp->ndev);
goto out;
}
@@ -875,9 +896,9 @@ static irqreturn_t axienet_rx_irq(int irq, void *_ndev)
axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
tasklet_schedule(&lp->dma_err_tasklet);
+ axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status);
}
out:
- axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status);
return IRQ_HANDLED;
}
@@ -891,10 +912,10 @@ static void axienet_dma_err_handler(unsigned long data);
* -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.
+ * 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)
{
@@ -910,7 +931,8 @@ static int axienet_open(struct net_device *ndev)
/* Disable the MDIO interface till Axi Ethernet Reset is completed.
* When we do an Axi Ethernet reset, it resets the complete core
* including the MDIO. If MDIO is not disabled when the reset
- * process is started, MDIO will be broken afterwards. */
+ * process is started, MDIO will be broken afterwards.
+ */
axienet_iow(lp, XAE_MDIO_MC_OFFSET,
(mdio_mcreg & (~XAE_MDIO_MC_MDIOEN_MASK)));
axienet_device_reset(ndev);
@@ -921,14 +943,20 @@ static int axienet_open(struct net_device *ndev)
return ret;
if (lp->phy_node) {
- lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node,
+ if (lp->phy_type == XAE_PHY_TYPE_GMII) {
+ lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node,
axienet_adjust_link, 0,
PHY_INTERFACE_MODE_GMII);
- if (!lp->phy_dev) {
- dev_err(lp->dev, "of_phy_connect() failed\n");
- return -ENODEV;
+ } else if (lp->phy_type == XAE_PHY_TYPE_RGMII_2_0) {
+ lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node,
+ axienet_adjust_link, 0,
+ PHY_INTERFACE_MODE_RGMII_ID);
}
- phy_start(lp->phy_dev);
+
+ if (!lp->phy_dev)
+ dev_err(lp->dev, "of_phy_connect() failed\n");
+ else
+ phy_start(lp->phy_dev);
}
/* Enable tasklets for Axi DMA error handling */
@@ -1013,15 +1041,15 @@ static int axienet_change_mtu(struct net_device *ndev, int new_mtu)
if (netif_running(ndev))
return -EBUSY;
- if (lp->jumbo_support) {
- if ((new_mtu > XAE_JUMBO_MTU) || (new_mtu < 64))
- return -EINVAL;
- ndev->mtu = new_mtu;
- } else {
- if ((new_mtu > XAE_MTU) || (new_mtu < 64))
- return -EINVAL;
- ndev->mtu = new_mtu;
- }
+
+ if ((new_mtu + VLAN_ETH_HLEN +
+ XAE_TRL_SIZE) > lp->rxmem)
+ return -EINVAL;
+
+ if ((new_mtu > XAE_JUMBO_MTU) || (new_mtu < 64))
+ return -EINVAL;
+
+ ndev->mtu = new_mtu;
return 0;
}
@@ -1031,8 +1059,8 @@ static int axienet_change_mtu(struct net_device *ndev, int new_mtu)
* 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.
+ * 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)
{
@@ -1046,6 +1074,20 @@ static void axienet_poll_controller(struct net_device *ndev)
}
#endif
+/* Ioctl MII Interface */
+static int axienet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct axienet_local *priv = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (!priv->phy_dev)
+ return -EOPNOTSUPP;
+
+ return phy_mii_ioctl(priv->phy_dev, rq, cmd);
+}
+
static const struct net_device_ops axienet_netdev_ops = {
.ndo_open = axienet_open,
.ndo_stop = axienet_stop,
@@ -1054,6 +1096,7 @@ static const struct net_device_ops axienet_netdev_ops = {
.ndo_set_mac_address = netdev_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_rx_mode = axienet_set_multicast_list,
+ .ndo_do_ioctl = axienet_ioctl,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = axienet_poll_controller,
#endif
@@ -1209,7 +1252,7 @@ axienet_ethtools_get_pauseparam(struct net_device *ndev,
* axienet_ethtools_set_pauseparam - Set device pause parameter(flow control)
* settings.
* @ndev: Pointer to net_device structure
- * @epauseparam:Pointer to ethtool_pauseparam structure
+ * @epauseparm:Pointer to ethtool_pauseparam structure
*
* This implements ethtool command for enabling flow control on Rx and Tx
* paths. Issue "ethtool -A ethX tx on|off" under linux prompt to execute this
@@ -1223,8 +1266,9 @@ axienet_ethtools_set_pauseparam(struct net_device *ndev,
struct axienet_local *lp = netdev_priv(ndev);
if (netif_running(ndev)) {
- printk(KERN_ERR "%s: Please stop netif before applying "
- "configruation\n", ndev->name);
+ dev_err(&ndev->dev,
+ "%s: Please stop netif before configuration\n",
+ ndev->name);
return -EFAULT;
}
@@ -1280,8 +1324,9 @@ static int axienet_ethtools_set_coalesce(struct net_device *ndev,
struct axienet_local *lp = netdev_priv(ndev);
if (netif_running(ndev)) {
- printk(KERN_ERR "%s: Please stop netif before applying "
- "configruation\n", ndev->name);
+ dev_err(&ndev->dev,
+ "%s: Please stop netif before configuration\n",
+ ndev->name);
return -EFAULT;
}
@@ -1350,7 +1395,8 @@ static void axienet_dma_err_handler(unsigned long data)
/* Disable the MDIO interface till Axi Ethernet Reset is completed.
* When we do an Axi Ethernet reset, it resets the complete core
* including the MDIO. So if MDIO is not disabled when the reset
- * process is started, MDIO will be broken afterwards. */
+ * process is started, MDIO will be broken afterwards.
+ */
axienet_iow(lp, XAE_MDIO_MC_OFFSET, (mdio_mcreg &
~XAE_MDIO_MC_MDIOEN_MASK));
@@ -1421,7 +1467,8 @@ static void axienet_dma_err_handler(unsigned long data)
axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr);
/* Populate the tail pointer and bring the Rx Axi DMA engine out of
- * halted state. This will make the Rx side ready for reception.*/
+ * halted state. This will make the Rx side ready for reception.
+ */
axienet_dma_out32(lp, XAXIDMA_RX_CDESC_OFFSET, lp->rx_bd_p);
cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET,
@@ -1431,7 +1478,8 @@ static void axienet_dma_err_handler(unsigned long data)
/* Write to the RS (Run-stop) bit in the Tx channel control register.
* Tx channel is now ready to run. But only after we write to the
- * tail pointer register that the Tx channel will start transmitting */
+ * tail pointer register that the Tx channel will start transmitting
+ */
axienet_dma_out32(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p);
cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET,
@@ -1447,7 +1495,8 @@ static void axienet_dma_err_handler(unsigned long data)
axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
/* Sync default options with HW but leave receiver and
- * transmitter disabled.*/
+ * transmitter disabled.
+ */
axienet_setoptions(ndev, lp->options &
~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
axienet_set_mac_address(ndev, NULL);
@@ -1456,77 +1505,84 @@ static void axienet_dma_err_handler(unsigned long data)
}
/**
- * axienet_of_probe - Axi Ethernet probe function.
- * @op: Pointer to platform device structure.
- * @match: Pointer to device id structure
+ * axienet_probe - Axi Ethernet probe function.
+ * @pdev: Pointer to platform device structure.
*
* returns: 0, on success
* Non-zero error value on failure.
*
* 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
+ * 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 axienet_of_probe(struct platform_device *op)
+static int axienet_probe(struct platform_device *pdev)
{
- __be32 *p;
- int size, ret = 0;
+ int ret;
struct device_node *np;
struct axienet_local *lp;
struct net_device *ndev;
- const void *addr;
+ u8 mac_addr[6];
+ struct resource *ethres, dmares;
+ u32 value;
ndev = alloc_etherdev(sizeof(*lp));
if (!ndev)
return -ENOMEM;
ether_setup(ndev);
- platform_set_drvdata(op, ndev);
+ platform_set_drvdata(pdev, ndev);
- SET_NETDEV_DEV(ndev, &op->dev);
+ SET_NETDEV_DEV(ndev, &pdev->dev);
ndev->flags &= ~IFF_MULTICAST; /* clear multicast */
- ndev->features = NETIF_F_SG | NETIF_F_FRAGLIST;
+ ndev->features = NETIF_F_FRAGLIST;
ndev->netdev_ops = &axienet_netdev_ops;
ndev->ethtool_ops = &axienet_ethtool_ops;
lp = netdev_priv(ndev);
lp->ndev = ndev;
- lp->dev = &op->dev;
+ lp->dev = &pdev->dev;
lp->options = XAE_OPTION_DEFAULTS;
/* Map device registers */
- lp->regs = of_iomap(op->dev.of_node, 0);
+ ethres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ lp->regs = devm_ioremap_resource(&pdev->dev, ethres);
if (!lp->regs) {
- dev_err(&op->dev, "could not map Axi Ethernet regs.\n");
- goto nodev;
+ dev_err(&pdev->dev, "could not map Axi Ethernet regs.\n");
+ ret = -ENOMEM;
+ goto free_netdev;
}
+
/* Setup checksum offload, but default to off if not specified */
lp->features = 0;
- p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,txcsum", NULL);
- if (p) {
- switch (be32_to_cpup(p)) {
+ ret = of_property_read_u32(pdev->dev.of_node, "xlnx,txcsum", &value);
+ if (!ret) {
+ dev_info(&pdev->dev, "TX_CSUM %d\n", value);
+
+ switch (value) {
case 1:
lp->csum_offload_on_tx_path =
XAE_FEATURE_PARTIAL_TX_CSUM;
lp->features |= XAE_FEATURE_PARTIAL_TX_CSUM;
/* Can checksum TCP/UDP over IPv4. */
- ndev->features |= NETIF_F_IP_CSUM;
+ ndev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
break;
case 2:
lp->csum_offload_on_tx_path =
XAE_FEATURE_FULL_TX_CSUM;
lp->features |= XAE_FEATURE_FULL_TX_CSUM;
/* Can checksum TCP/UDP over IPv4. */
- ndev->features |= NETIF_F_IP_CSUM;
+ ndev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
break;
default:
lp->csum_offload_on_tx_path = XAE_NO_CSUM_OFFLOAD;
}
}
- p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,rxcsum", NULL);
- if (p) {
- switch (be32_to_cpup(p)) {
+ ret = of_property_read_u32(pdev->dev.of_node, "xlnx,rxcsum", &value);
+ if (!ret) {
+ dev_info(&pdev->dev, "RX_CSUM %d\n", value);
+
+ switch (value) {
case 1:
lp->csum_offload_on_rx_path =
XAE_FEATURE_PARTIAL_RX_CSUM;
@@ -1542,85 +1598,80 @@ static int axienet_of_probe(struct platform_device *op)
}
}
/* For supporting jumbo frames, the Axi Ethernet hardware must have
- * a larger Rx/Tx Memory. Typically, the size must be more than or
- * equal to 16384 bytes, so that we can enable jumbo option and start
- * supporting jumbo frames. Here we check for memory allocated for
- * Rx/Tx in the hardware from the device-tree and accordingly set
- * flags. */
- p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,rxmem", NULL);
- if (p) {
- if ((be32_to_cpup(p)) >= 0x4000)
- lp->jumbo_support = 1;
+ * a larger Rx/Tx Memory. Typically, the size must be large so that
+ * we can enable jumbo option and start supporting jumbo frames.
+ * Here we check for memory allocated for Rx/Tx in the hardware from
+ * the device-tree and accordingly set flags.
+ */
+ of_property_read_u32(pdev->dev.of_node, "xlnx,rxmem", &lp->rxmem);
+ of_property_read_u32(pdev->dev.of_node, "xlnx,temac-type",
+ &lp->temac_type);
+ of_property_read_u32(pdev->dev.of_node, "xlnx,phy-type",
+ &lp->phy_type);
+
+ /* Find the DMA node, map the DMA registers, and decode DMA IRQs */
+ np = of_parse_phandle(pdev->dev.of_node, "axistream-connected", 0);
+ if (IS_ERR(np)) {
+ dev_err(&pdev->dev, "could not find DMA node\n");
+ ret = PTR_ERR(np);
+ goto free_netdev;
}
- 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;
+ ret = of_address_to_resource(np, 0, &dmares);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to get DMA resource\n");
+ goto free_netdev;
}
- lp->dma_regs = 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->dma_regs = devm_ioremap_resource(&pdev->dev, &dmares);
+ if (!lp->dma_regs) {
+ dev_err(&pdev->dev, "could not map DMA regs\n");
+ ret = -ENOMEM;
+ goto free_netdev;
}
lp->rx_irq = irq_of_parse_and_map(np, 1);
lp->tx_irq = irq_of_parse_and_map(np, 0);
of_node_put(np);
if ((lp->rx_irq <= 0) || (lp->tx_irq <= 0)) {
- dev_err(&op->dev, "could not determine irqs\n");
+ dev_err(&pdev->dev, "could not determine irqs\n");
ret = -ENOMEM;
- goto err_iounmap_2;
+ goto free_netdev;
}
/* Retrieve the MAC address */
- addr = of_get_property(op->dev.of_node, "local-mac-address", &size);
- if ((!addr) || (size != 6)) {
- dev_err(&op->dev, "could not find MAC address\n");
- ret = -ENODEV;
- goto err_iounmap_2;
+ ret = of_property_read_u8_array(pdev->dev.of_node,
+ "local-mac-address", mac_addr, 6);
+ if (ret) {
+ dev_err(&pdev->dev, "could not find MAC address\n");
+ goto free_netdev;
}
- axienet_set_mac_address(ndev, (void *) addr);
+ axienet_set_mac_address(ndev, (void *) mac_addr);
lp->coalesce_count_rx = XAXIDMA_DFT_RX_THRESHOLD;
lp->coalesce_count_tx = XAXIDMA_DFT_TX_THRESHOLD;
- lp->phy_node = of_parse_phandle(op->dev.of_node, "phy-handle", 0);
- ret = axienet_mdio_setup(lp, op->dev.of_node);
- if (ret)
- dev_warn(&op->dev, "error registering MDIO bus\n");
+ lp->phy_node = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
+ if (lp->phy_node) {
+ ret = axienet_mdio_setup(lp, pdev->dev.of_node);
+ if (ret)
+ dev_warn(&pdev->dev, "error registering MDIO bus\n");
+ }
ret = register_netdev(lp->ndev);
if (ret) {
dev_err(lp->dev, "register_netdev() error (%i)\n", ret);
- goto err_iounmap_2;
+ goto free_netdev;
}
return 0;
-err_iounmap_2:
- if (lp->dma_regs)
- iounmap(lp->dma_regs);
-err_iounmap:
- iounmap(lp->regs);
-nodev:
+free_netdev:
free_netdev(ndev);
- ndev = NULL;
+
return ret;
}
-static int axienet_of_remove(struct platform_device *op)
+static int axienet_remove(struct platform_device *pdev)
{
- struct net_device *ndev = platform_get_drvdata(op);
+ struct net_device *ndev = platform_get_drvdata(pdev);
struct axienet_local *lp = netdev_priv(ndev);
axienet_mdio_teardown(lp);
@@ -1630,17 +1681,14 @@ static int axienet_of_remove(struct platform_device *op)
of_node_put(lp->phy_node);
lp->phy_node = NULL;
- iounmap(lp->regs);
- if (lp->dma_regs)
- iounmap(lp->dma_regs);
free_netdev(ndev);
return 0;
}
-static struct platform_driver axienet_of_driver = {
- .probe = axienet_of_probe,
- .remove = axienet_of_remove,
+static struct platform_driver axienet_driver = {
+ .probe = axienet_probe,
+ .remove = axienet_remove,
.driver = {
.owner = THIS_MODULE,
.name = "xilinx_axienet",
@@ -1648,7 +1696,7 @@ static struct platform_driver axienet_of_driver = {
},
};
-module_platform_driver(axienet_of_driver);
+module_platform_driver(axienet_driver);
MODULE_DESCRIPTION("Xilinx Axi Ethernet driver");
MODULE_AUTHOR("Xilinx");
@@ -120,7 +120,8 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
* @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.
+ * 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.
@@ -128,11 +129,12 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
{
int ret;
- u32 clk_div, host_clock;
- u32 *property_p;
+ u32 clk_div;
struct mii_bus *bus;
struct resource res;
struct device_node *np1;
+ /* the ethernet controller device node */
+ struct device_node *npp = NULL;
/* clk_div can be calculated by deriving it from the equation:
* fMDIO = fHOST / ((1 + clk_div) * 2)
@@ -158,42 +160,50 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
* fHOST can be read from the flattened device tree as property
* "clock-frequency" from the CPU
*/
-
- np1 = of_find_node_by_name(NULL, "cpu");
- if (!np1) {
- printk(KERN_WARNING "%s(): Could not find CPU device node.",
- __func__);
- printk(KERN_WARNING "Setting MDIO clock divisor to "
- "default %d\n", DEFAULT_CLOCK_DIVISOR);
- clk_div = DEFAULT_CLOCK_DIVISOR;
- goto issue;
- }
- property_p = (u32 *) of_get_property(np1, "clock-frequency", NULL);
- if (!property_p) {
- printk(KERN_WARNING "%s(): Could not find CPU property: "
- "clock-frequency.", __func__);
- printk(KERN_WARNING "Setting MDIO clock divisor to "
- "default %d\n", DEFAULT_CLOCK_DIVISOR);
+ np1 = of_get_parent(lp->phy_node);
+ if (np1)
+ npp = of_get_parent(np1);
+ if (!npp) {
+ dev_warn(lp->dev,
+ "Could not find ethernet controller device node.");
+ dev_warn(lp->dev,
+ "Setting MDIO clock divisor to default %d\n",
+ DEFAULT_CLOCK_DIVISOR);
clk_div = DEFAULT_CLOCK_DIVISOR;
- of_node_put(np1);
- goto issue;
+ } else {
+ u32 *property_p;
+
+ property_p = (uint32_t *)of_get_property(npp,
+ "clock-frequency", NULL);
+ if (!property_p) {
+ dev_warn(lp->dev,
+ "Could not find clock ethernet "
+ "controller property.");
+ dev_warn(lp->dev,
+ "Setting MDIO clock divisor to default %d\n",
+ DEFAULT_CLOCK_DIVISOR);
+ clk_div = DEFAULT_CLOCK_DIVISOR;
+ } else {
+ u32 host_clock = be32_to_cpup(property_p);
+
+ clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1;
+
+ /* If there is any remainder from the division of
+ * fHOST / (MAX_MDIO_FREQ * 2), then we need to add 1
+ * to the clock divisor or we will surely be
+ * above 2.5 MHz
+ */
+ if (host_clock % (MAX_MDIO_FREQ * 2))
+ clk_div++;
+ dev_dbg(lp->dev,
+ "Setting MDIO clock divisor to %u "
+ "based on %u Hz host clock.\n",
+ clk_div, host_clock);
+ }
}
- host_clock = be32_to_cpup(property_p);
- clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1;
- /* If there is any remainder from the division of
- * fHOST / (MAX_MDIO_FREQ * 2), then we need to add
- * 1 to the clock divisor or we will surely be above 2.5 MHz */
- if (host_clock % (MAX_MDIO_FREQ * 2))
- clk_div++;
-
- printk(KERN_DEBUG "%s(): Setting MDIO clock divisor to %u based "
- "on %u Hz host clock.\n", __func__, clk_div, host_clock);
-
- of_node_put(np1);
-issue:
- axienet_iow(lp, XAE_MDIO_MC_OFFSET,
- (((u32) clk_div) | XAE_MDIO_MC_MDIOEN_MASK));
+ axienet_iow(lp, XAE_MDIO_MC_OFFSET, (((u32)clk_div) |
+ XAE_MDIO_MC_MDIOEN_MASK));
ret = axienet_mdio_wait_until_ready(lp);
if (ret < 0)
@@ -203,8 +213,7 @@ issue:
if (!bus)
return -ENOMEM;
- np1 = of_get_parent(lp->phy_node);
- of_address_to_resource(np1, 0, &res);
+ of_address_to_resource(npp, 0, &res);
snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx",
(unsigned long long) res.start);
@@ -233,7 +242,6 @@ issue:
void axienet_mdio_teardown(struct axienet_local *lp)
{
mdiobus_unregister(lp->mii_bus);
- kfree(lp->mii_bus->irq);
mdiobus_free(lp->mii_bus);
lp->mii_bus = NULL;
}
@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
+#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
@@ -44,7 +45,7 @@
#define XEL_RPLR_OFFSET 0x100C /* Rx packet length */
#define XEL_RSR_OFFSET 0x17FC /* Rx status */
-#define XEL_BUFFER_OFFSET 0x0800 /* Next Tx/Rx buffer's offset */
+#define XEL_BUFFER_OFFSET 0x0800 /* Next Tx/Rx buffer offset */
/* MDIO Address Register Bit Masks */
#define XEL_MDIOADDR_REGADR_MASK 0x0000001F /* Register Address */
@@ -110,8 +111,8 @@
* @next_rx_buf_to_use: next Rx buffer to read from
* @base_addr: base address of the Emaclite device
* @reset_lock: lock used for synchronization
- * @deferred_skb: holds an skb (for transmission at a later time) when the
- * Tx buffer is not free
+ * @deferred_skb: holds an skb (for transmission at a later time) when
+ * the Tx buffer is not free
* @phy_dev: pointer to the PHY device
* @phy_node: pointer to the PHY device node
* @mii_bus: pointer to the MII bus
@@ -151,8 +152,8 @@ struct net_local {
* xemaclite_enable_interrupts - Enable the interrupts for the EmacLite device
* @drvdata: Pointer to the Emaclite device private data
*
- * This function enables the Tx and Rx interrupts for the Emaclite device along
- * with the Global Interrupt Enable.
+ * This function enables the Tx and Rx interrupts for the Emaclite device
+ * along with the Global Interrupt Enable.
*/
static void xemaclite_enable_interrupts(struct net_local *drvdata)
{
@@ -174,7 +175,8 @@ static void xemaclite_enable_interrupts(struct net_local *drvdata)
}
/* Enable the Rx interrupts for the first buffer */
- __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET);
+ __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr +
+ XEL_RSR_OFFSET);
/* Enable the Rx interrupts for the second Buffer if
* configured in HW */
@@ -188,7 +190,8 @@ static void xemaclite_enable_interrupts(struct net_local *drvdata)
}
/**
- * xemaclite_disable_interrupts - Disable the interrupts for the EmacLite device
+ * xemaclite_disable_interrupts - Disable the interrupts for the EmacLite
+ * device.
* @drvdata: Pointer to the Emaclite device private data
*
* This function disables the Tx and Rx interrupts for the Emaclite device,
@@ -209,8 +212,8 @@ static void xemaclite_disable_interrupts(struct net_local *drvdata)
/* Disable the Tx interrupts for the second Buffer
* if configured in HW */
if (drvdata->tx_ping_pong != 0) {
- reg_data = __raw_readl(drvdata->base_addr + XEL_BUFFER_OFFSET +
- XEL_TSR_OFFSET);
+ reg_data = __raw_readl(drvdata->base_addr +
+ XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
__raw_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK),
drvdata->base_addr + XEL_BUFFER_OFFSET +
XEL_TSR_OFFSET);
@@ -225,8 +228,8 @@ static void xemaclite_disable_interrupts(struct net_local *drvdata)
* if configured in HW */
if (drvdata->rx_ping_pong != 0) {
- reg_data = __raw_readl(drvdata->base_addr + XEL_BUFFER_OFFSET +
- XEL_RSR_OFFSET);
+ reg_data = __raw_readl(drvdata->base_addr +
+ XEL_BUFFER_OFFSET + XEL_RSR_OFFSET);
__raw_writel(reg_data & (~XEL_RSR_RECV_IE_MASK),
drvdata->base_addr + XEL_BUFFER_OFFSET +
XEL_RSR_OFFSET);
@@ -234,7 +237,8 @@ static void xemaclite_disable_interrupts(struct net_local *drvdata)
}
/**
- * xemaclite_aligned_write - Write from 16-bit aligned to 32-bit aligned address
+ * xemaclite_aligned_write - Write from 16-bit aligned to 32-bit aligned
+ * address.
* @src_ptr: Void pointer to the 16-bit aligned source address
* @dest_ptr: Pointer to the 32-bit aligned destination address
* @length: Number bytes to write from source to destination
@@ -283,8 +287,8 @@ static void xemaclite_aligned_write(void *src_ptr, u32 *dest_ptr,
* @dest_ptr: Pointer to the 16-bit aligned destination address
* @length: Number bytes to read from source to destination
*
- * This function reads data from a 32-bit aligned address in the EmacLite device
- * to a 16-bit aligned buffer.
+ * This function reads data from a 32-bit aligned address in the EmacLite
+ * device to a 16-bit aligned buffer.
*/
static void xemaclite_aligned_read(u32 *src_ptr, u8 *dest_ptr,
unsigned length)
@@ -326,14 +330,14 @@ static void xemaclite_aligned_read(u32 *src_ptr, u8 *dest_ptr,
* @data: Pointer to the data to be sent
* @byte_count: Total frame size, including header
*
- * This function checks if the Tx buffer of the Emaclite device is free to send
- * data. If so, it fills the Tx buffer with data for transmission. Otherwise, it
- * returns an error.
+ * This function checks if the Tx buffer of the Emaclite device is free to
+ * send data. If so, it fills the Tx buffer with data for transmission.
+ * Otherwise, it returns an error.
*
* Return: 0 upon success or -1 if the buffer(s) are full.
*
- * Note: The maximum Tx packet size can not be more than Ethernet header
- * (14 Bytes) + Maximum MTU (1500 bytes). This is excluding FCS.
+ * Note: The maximum Tx packet size can not be more than Ethernet header
+ * (14 Bytes) + Maximum MTU (1500 bytes). This is excluding FCS.
*/
static int xemaclite_send_data(struct net_local *drvdata, u8 *data,
unsigned int byte_count)
@@ -687,7 +691,8 @@ static irqreturn_t xemaclite_interrupt(int irq, void *dev_id)
}
/* Check if the Transmission for the second buffer is completed */
- tx_status = __raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
+ tx_status = __raw_readl(base_addr + XEL_BUFFER_OFFSET +
+ XEL_TSR_OFFSET);
if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) &&
(tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) {
@@ -1053,7 +1058,7 @@ static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev)
* current transmission is complete */
netif_stop_queue(dev);
lp->deferred_skb = new_skb;
- /* Take the time stamp now, since we can't do this in an ISR. */
+ /* Take the time stamp now, since we can't do this in an ISR */
skb_tx_timestamp(new_skb);
spin_unlock_irqrestore(&lp->reset_lock, flags);
return 0;
@@ -1090,7 +1095,7 @@ static void xemaclite_remove_ndev(struct net_device *ndev)
* This function looks for a property in the device node and returns the value
* of the property if its found or 0 if the property is not found.
*
- * Return: Value of the parameter if the parameter is found, or 0 otherwise
+ * Return: Value of the parameter if the parameter is found, or 0 otherwise
*/
static bool get_bool(struct platform_device *ofdev, const char *s)
{
@@ -1172,7 +1177,7 @@ static int xemaclite_of_probe(struct platform_device *ofdev)
if (mac_address)
/* Set the MAC address. */
- memcpy(ndev->dev_addr, mac_address, ETH_ALEN);
+ memcpy(ndev->dev_addr, mac_address, 6);
else
dev_warn(dev, "No MAC address found\n");
new file mode 100644
@@ -0,0 +1,2912 @@
+/*
+ * Xilinx Ethernet: Linux driver for Ethernet.
+ *
+ * Author: Xilinx, Inc.
+ *
+ * 2010 (c) Xilinx, Inc. This file is licensed uner the terms of the GNU
+ * General Public License version 2. This program is licensed "as is"
+ * without any warranty of any kind, whether express or implied.
+ *
+ * This is a driver for xilinx processor sub-system (ps) ethernet device.
+ * This driver is mainly used in Linux 2.6.30 and above and it does _not_
+ * support Linux 2.4 kernel due to certain new features (e.g. NAPI) is
+ * introduced in this driver.
+ *
+ * TODO:
+ * 1. JUMBO frame is not enabled per EPs spec. Please update it if this
+ * support is added in and set MAX_MTU to 9000.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/phy.h>
+#include <linux/mii.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/ethtool.h>
+#include <linux/vmalloc.h>
+#include <linux/version.h>
+#include <linux/of.h>
+#include <linux/interrupt.h>
+#include <linux/clocksource.h>
+#include <linux/net_tstamp.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/of_net.h>
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/timer.h>
+
+/************************** Constant Definitions *****************************/
+
+/* Must be shorter than length of ethtool_drvinfo.driver field to fit */
+#define DRIVER_NAME "xemacps"
+#define DRIVER_DESCRIPTION "Xilinx Tri-Mode Ethernet MAC driver"
+#define DRIVER_VERSION "1.00a"
+
+/* Transmission timeout is 3 seconds. */
+#define TX_TIMEOUT (3*HZ)
+
+/* for RX skb IP header word-aligned */
+#define RX_IP_ALIGN_OFFSET 2
+
+/* DMA buffer descriptors must be aligned on a 4-byte boundary. */
+#define ALIGNMENT_BD 8
+
+/* Maximum value for hash bits. 2**6 */
+#define XEMACPS_MAX_HASH_BITS 64
+
+/* MDC clock division
+ * currently supporting 8, 16, 32, 48, 64, 96, 128, 224.
+ */
+enum { MDC_DIV_8 = 0, MDC_DIV_16, MDC_DIV_32, MDC_DIV_48,
+MDC_DIV_64, MDC_DIV_96, MDC_DIV_128, MDC_DIV_224 };
+
+/* Specify the receive buffer size in bytes, 64, 128, 192, 10240 */
+#define XEMACPS_RX_BUF_SIZE 1536
+
+/* Number of receive buffer bytes as a unit, this is HW setup */
+#define XEMACPS_RX_BUF_UNIT 64
+
+/* Default SEND and RECV buffer descriptors (BD) numbers.
+ * BD Space needed is (XEMACPS_SEND_BD_CNT+XEMACPS_RECV_BD_CNT)*8
+ */
+#undef DEBUG
+#define DEBUG
+
+#define XEMACPS_SEND_BD_CNT 256
+#define XEMACPS_RECV_BD_CNT 256
+
+#define XEMACPS_NAPI_WEIGHT 64
+
+/* Register offset definitions. Unless otherwise noted, register access is
+ * 32 bit. Names are self explained here.
+ */
+#define XEMACPS_NWCTRL_OFFSET 0x00000000 /* Network Control reg */
+#define XEMACPS_NWCFG_OFFSET 0x00000004 /* Network Config reg */
+#define XEMACPS_NWSR_OFFSET 0x00000008 /* Network Status reg */
+#define XEMACPS_USERIO_OFFSET 0x0000000C /* User IO reg */
+#define XEMACPS_DMACR_OFFSET 0x00000010 /* DMA Control reg */
+#define XEMACPS_TXSR_OFFSET 0x00000014 /* TX Status reg */
+#define XEMACPS_RXQBASE_OFFSET 0x00000018 /* RX Q Base address reg */
+#define XEMACPS_TXQBASE_OFFSET 0x0000001C /* TX Q Base address reg */
+#define XEMACPS_RXSR_OFFSET 0x00000020 /* RX Status reg */
+#define XEMACPS_ISR_OFFSET 0x00000024 /* Interrupt Status reg */
+#define XEMACPS_IER_OFFSET 0x00000028 /* Interrupt Enable reg */
+#define XEMACPS_IDR_OFFSET 0x0000002C /* Interrupt Disable reg */
+#define XEMACPS_IMR_OFFSET 0x00000030 /* Interrupt Mask reg */
+#define XEMACPS_PHYMNTNC_OFFSET 0x00000034 /* Phy Maintaince reg */
+#define XEMACPS_RXPAUSE_OFFSET 0x00000038 /* RX Pause Time reg */
+#define XEMACPS_TXPAUSE_OFFSET 0x0000003C /* TX Pause Time reg */
+#define XEMACPS_HASHL_OFFSET 0x00000080 /* Hash Low address reg */
+#define XEMACPS_HASHH_OFFSET 0x00000084 /* Hash High address reg */
+#define XEMACPS_LADDR1L_OFFSET 0x00000088 /* Specific1 addr low */
+#define XEMACPS_LADDR1H_OFFSET 0x0000008C /* Specific1 addr high */
+#define XEMACPS_LADDR2L_OFFSET 0x00000090 /* Specific2 addr low */
+#define XEMACPS_LADDR2H_OFFSET 0x00000094 /* Specific2 addr high */
+#define XEMACPS_LADDR3L_OFFSET 0x00000098 /* Specific3 addr low */
+#define XEMACPS_LADDR3H_OFFSET 0x0000009C /* Specific3 addr high */
+#define XEMACPS_LADDR4L_OFFSET 0x000000A0 /* Specific4 addr low */
+#define XEMACPS_LADDR4H_OFFSET 0x000000A4 /* Specific4 addr high */
+#define XEMACPS_MATCH1_OFFSET 0x000000A8 /* Type ID1 Match reg */
+#define XEMACPS_MATCH2_OFFSET 0x000000AC /* Type ID2 Match reg */
+#define XEMACPS_MATCH3_OFFSET 0x000000B0 /* Type ID3 Match reg */
+#define XEMACPS_MATCH4_OFFSET 0x000000B4 /* Type ID4 Match reg */
+#define XEMACPS_WOL_OFFSET 0x000000B8 /* Wake on LAN reg */
+#define XEMACPS_STRETCH_OFFSET 0x000000BC /* IPG Stretch reg */
+#define XEMACPS_SVLAN_OFFSET 0x000000C0 /* Stacked VLAN reg */
+#define XEMACPS_MODID_OFFSET 0x000000FC /* Module ID reg */
+#define XEMACPS_OCTTXL_OFFSET 0x00000100 /* Octects transmitted Low
+ reg */
+#define XEMACPS_OCTTXH_OFFSET 0x00000104 /* Octects transmitted High
+ reg */
+#define XEMACPS_TXCNT_OFFSET 0x00000108 /* Error-free Frmaes
+ transmitted counter */
+#define XEMACPS_TXBCCNT_OFFSET 0x0000010C /* Error-free Broadcast
+ Frames counter*/
+#define XEMACPS_TXMCCNT_OFFSET 0x00000110 /* Error-free Multicast
+ Frame counter */
+#define XEMACPS_TXPAUSECNT_OFFSET 0x00000114 /* Pause Frames Transmitted
+ Counter */
+#define XEMACPS_TX64CNT_OFFSET 0x00000118 /* Error-free 64 byte Frames
+ Transmitted counter */
+#define XEMACPS_TX65CNT_OFFSET 0x0000011C /* Error-free 65-127 byte
+ Frames Transmitted counter */
+#define XEMACPS_TX128CNT_OFFSET 0x00000120 /* Error-free 128-255 byte
+ Frames Transmitted counter */
+#define XEMACPS_TX256CNT_OFFSET 0x00000124 /* Error-free 256-511 byte
+ Frames transmitted counter */
+#define XEMACPS_TX512CNT_OFFSET 0x00000128 /* Error-free 512-1023 byte
+ Frames transmitted counter */
+#define XEMACPS_TX1024CNT_OFFSET 0x0000012C /* Error-free 1024-1518 byte
+ Frames transmitted counter */
+#define XEMACPS_TX1519CNT_OFFSET 0x00000130 /* Error-free larger than
+ 1519 byte Frames transmitted
+ Counter */
+#define XEMACPS_TXURUNCNT_OFFSET 0x00000134 /* TX under run error
+ Counter */
+#define XEMACPS_SNGLCOLLCNT_OFFSET 0x00000138 /* Single Collision Frame
+ Counter */
+#define XEMACPS_MULTICOLLCNT_OFFSET 0x0000013C /* Multiple Collision Frame
+ Counter */
+#define XEMACPS_EXCESSCOLLCNT_OFFSET 0x00000140 /* Excessive Collision Frame
+ Counter */
+#define XEMACPS_LATECOLLCNT_OFFSET 0x00000144 /* Late Collision Frame
+ Counter */
+#define XEMACPS_TXDEFERCNT_OFFSET 0x00000148 /* Deferred Transmission
+ Frame Counter */
+#define XEMACPS_CSENSECNT_OFFSET 0x0000014C /* Carrier Sense Error
+ Counter */
+#define XEMACPS_OCTRXL_OFFSET 0x00000150 /* Octects Received register
+ Low */
+#define XEMACPS_OCTRXH_OFFSET 0x00000154 /* Octects Received register
+ High */
+#define XEMACPS_RXCNT_OFFSET 0x00000158 /* Error-free Frames
+ Received Counter */
+#define XEMACPS_RXBROADCNT_OFFSET 0x0000015C /* Error-free Broadcast
+ Frames Received Counter */
+#define XEMACPS_RXMULTICNT_OFFSET 0x00000160 /* Error-free Multicast
+ Frames Received Counter */
+#define XEMACPS_RXPAUSECNT_OFFSET 0x00000164 /* Pause Frames
+ Received Counter */
+#define XEMACPS_RX64CNT_OFFSET 0x00000168 /* Error-free 64 byte Frames
+ Received Counter */
+#define XEMACPS_RX65CNT_OFFSET 0x0000016C /* Error-free 65-127 byte
+ Frames Received Counter */
+#define XEMACPS_RX128CNT_OFFSET 0x00000170 /* Error-free 128-255 byte
+ Frames Received Counter */
+#define XEMACPS_RX256CNT_OFFSET 0x00000174 /* Error-free 256-512 byte
+ Frames Received Counter */
+#define XEMACPS_RX512CNT_OFFSET 0x00000178 /* Error-free 512-1023 byte
+ Frames Received Counter */
+#define XEMACPS_RX1024CNT_OFFSET 0x0000017C /* Error-free 1024-1518 byte
+ Frames Received Counter */
+#define XEMACPS_RX1519CNT_OFFSET 0x00000180 /* Error-free 1519-max byte
+ Frames Received Counter */
+#define XEMACPS_RXUNDRCNT_OFFSET 0x00000184 /* Undersize Frames Received
+ Counter */
+#define XEMACPS_RXOVRCNT_OFFSET 0x00000188 /* Oversize Frames Received
+ Counter */
+#define XEMACPS_RXJABCNT_OFFSET 0x0000018C /* Jabbers Received
+ Counter */
+#define XEMACPS_RXFCSCNT_OFFSET 0x00000190 /* Frame Check Sequence
+ Error Counter */
+#define XEMACPS_RXLENGTHCNT_OFFSET 0x00000194 /* Length Field Error
+ Counter */
+#define XEMACPS_RXSYMBCNT_OFFSET 0x00000198 /* Symbol Error Counter */
+#define XEMACPS_RXALIGNCNT_OFFSET 0x0000019C /* Alignment Error
+ Counter */
+#define XEMACPS_RXRESERRCNT_OFFSET 0x000001A0 /* Receive Resource Error
+ Counter */
+#define XEMACPS_RXORCNT_OFFSET 0x000001A4 /* Receive Overrun */
+#define XEMACPS_RXIPCCNT_OFFSET 0x000001A8 /* IP header Checksum Error
+ Counter */
+#define XEMACPS_RXTCPCCNT_OFFSET 0x000001AC /* TCP Checksum Error
+ Counter */
+#define XEMACPS_RXUDPCCNT_OFFSET 0x000001B0 /* UDP Checksum Error
+ Counter */
+
+#define XEMACPS_1588S_OFFSET 0x000001D0 /* 1588 Timer Seconds */
+#define XEMACPS_1588NS_OFFSET 0x000001D4 /* 1588 Timer Nanoseconds */
+#define XEMACPS_1588ADJ_OFFSET 0x000001D8 /* 1588 Timer Adjust */
+#define XEMACPS_1588INC_OFFSET 0x000001DC /* 1588 Timer Increment */
+#define XEMACPS_PTPETXS_OFFSET 0x000001E0 /* PTP Event Frame
+ Transmitted Seconds */
+#define XEMACPS_PTPETXNS_OFFSET 0x000001E4 /* PTP Event Frame
+ Transmitted Nanoseconds */
+#define XEMACPS_PTPERXS_OFFSET 0x000001E8 /* PTP Event Frame Received
+ Seconds */
+#define XEMACPS_PTPERXNS_OFFSET 0x000001EC /* PTP Event Frame Received
+ Nanoseconds */
+#define XEMACPS_PTPPTXS_OFFSET 0x000001E0 /* PTP Peer Frame
+ Transmitted Seconds */
+#define XEMACPS_PTPPTXNS_OFFSET 0x000001E4 /* PTP Peer Frame
+ Transmitted Nanoseconds */
+#define XEMACPS_PTPPRXS_OFFSET 0x000001E8 /* PTP Peer Frame Received
+ Seconds */
+#define XEMACPS_PTPPRXNS_OFFSET 0x000001EC /* PTP Peer Frame Received
+ Nanoseconds */
+
+/* network control register bit definitions */
+#define XEMACPS_NWCTRL_FLUSH_DPRAM_MASK 0x00040000
+#define XEMACPS_NWCTRL_RXTSTAMP_MASK 0x00008000 /* RX Timestamp in CRC */
+#define XEMACPS_NWCTRL_ZEROPAUSETX_MASK 0x00001000 /* Transmit zero quantum
+ pause frame */
+#define XEMACPS_NWCTRL_PAUSETX_MASK 0x00000800 /* Transmit pause frame */
+#define XEMACPS_NWCTRL_HALTTX_MASK 0x00000400 /* Halt transmission
+ after current frame */
+#define XEMACPS_NWCTRL_STARTTX_MASK 0x00000200 /* Start tx (tx_go) */
+
+#define XEMACPS_NWCTRL_STATWEN_MASK 0x00000080 /* Enable writing to
+ stat counters */
+#define XEMACPS_NWCTRL_STATINC_MASK 0x00000040 /* Increment statistic
+ registers */
+#define XEMACPS_NWCTRL_STATCLR_MASK 0x00000020 /* Clear statistic
+ registers */
+#define XEMACPS_NWCTRL_MDEN_MASK 0x00000010 /* Enable MDIO port */
+#define XEMACPS_NWCTRL_TXEN_MASK 0x00000008 /* Enable transmit */
+#define XEMACPS_NWCTRL_RXEN_MASK 0x00000004 /* Enable receive */
+#define XEMACPS_NWCTRL_LOOPEN_MASK 0x00000002 /* local loopback */
+
+/* name network configuration register bit definitions */
+#define XEMACPS_NWCFG_BADPREAMBEN_MASK 0x20000000 /* disable rejection of
+ non-standard preamble */
+#define XEMACPS_NWCFG_IPDSTRETCH_MASK 0x10000000 /* enable transmit IPG */
+#define XEMACPS_NWCFG_FCSIGNORE_MASK 0x04000000 /* disable rejection of
+ FCS error */
+#define XEMACPS_NWCFG_HDRXEN_MASK 0x02000000 /* RX half duplex */
+#define XEMACPS_NWCFG_RXCHKSUMEN_MASK 0x01000000 /* enable RX checksum
+ offload */
+#define XEMACPS_NWCFG_PAUSECOPYDI_MASK 0x00800000 /* Do not copy pause
+ Frames to memory */
+#define XEMACPS_NWCFG_MDC_SHIFT_MASK 18 /* shift bits for MDC */
+#define XEMACPS_NWCFG_MDCCLKDIV_MASK 0x001C0000 /* MDC Mask PCLK divisor */
+#define XEMACPS_NWCFG_FCSREM_MASK 0x00020000 /* Discard FCS from
+ received frames */
+#define XEMACPS_NWCFG_LENGTHERRDSCRD_MASK 0x00010000
+/* RX length error discard */
+#define XEMACPS_NWCFG_RXOFFS_MASK 0x0000C000 /* RX buffer offset */
+#define XEMACPS_NWCFG_PAUSEEN_MASK 0x00002000 /* Enable pause TX */
+#define XEMACPS_NWCFG_RETRYTESTEN_MASK 0x00001000 /* Retry test */
+#define XEMACPS_NWCFG_1000_MASK 0x00000400 /* Gigbit mode */
+#define XEMACPS_NWCFG_EXTADDRMATCHEN_MASK 0x00000200
+/* External address match enable */
+#define XEMACPS_NWCFG_UCASTHASHEN_MASK 0x00000080 /* Receive unicast hash
+ frames */
+#define XEMACPS_NWCFG_MCASTHASHEN_MASK 0x00000040 /* Receive multicast hash
+ frames */
+#define XEMACPS_NWCFG_BCASTDI_MASK 0x00000020 /* Do not receive
+ broadcast frames */
+#define XEMACPS_NWCFG_COPYALLEN_MASK 0x00000010 /* Copy all frames */
+
+#define XEMACPS_NWCFG_NVLANDISC_MASK 0x00000004 /* Receive only VLAN
+ frames */
+#define XEMACPS_NWCFG_FDEN_MASK 0x00000002 /* Full duplex */
+#define XEMACPS_NWCFG_100_MASK 0x00000001 /* 10 or 100 Mbs */
+
+/* network status register bit definitaions */
+#define XEMACPS_NWSR_MDIOIDLE_MASK 0x00000004 /* PHY management idle */
+#define XEMACPS_NWSR_MDIO_MASK 0x00000002 /* Status of mdio_in */
+
+/* MAC address register word 1 mask */
+#define XEMACPS_LADDR_MACH_MASK 0x0000FFFF /* Address bits[47:32]
+ bit[31:0] are in BOTTOM */
+
+/* DMA control register bit definitions */
+#define XEMACPS_DMACR_RXBUF_MASK 0x00FF0000 /* Mask bit for RX buffer
+ size */
+#define XEMACPS_DMACR_RXBUF_SHIFT 16 /* Shift bit for RX buffer
+ size */
+#define XEMACPS_DMACR_TCPCKSUM_MASK 0x00000800 /* enable/disable TX
+ checksum offload */
+#define XEMACPS_DMACR_TXSIZE_MASK 0x00000400 /* TX buffer memory size */
+#define XEMACPS_DMACR_RXSIZE_MASK 0x00000300 /* RX buffer memory size */
+#define XEMACPS_DMACR_ENDIAN_MASK 0x00000080 /* Endian configuration */
+#define XEMACPS_DMACR_BLENGTH_MASK 0x0000001F /* Buffer burst length */
+#define XEMACPS_DMACR_BLENGTH_INCR16 0x00000010 /* Buffer burst length */
+#define XEMACPS_DMACR_BLENGTH_INCR8 0x00000008 /* Buffer burst length */
+#define XEMACPS_DMACR_BLENGTH_INCR4 0x00000004 /* Buffer burst length */
+#define XEMACPS_DMACR_BLENGTH_SINGLE 0x00000002 /* Buffer burst length */
+
+/* transmit status register bit definitions */
+#define XEMACPS_TXSR_HRESPNOK_MASK 0x00000100 /* Transmit hresp not OK */
+#define XEMACPS_TXSR_COL1000_MASK 0x00000080 /* Collision Gbs mode */
+#define XEMACPS_TXSR_URUN_MASK 0x00000040 /* Transmit underrun */
+#define XEMACPS_TXSR_TXCOMPL_MASK 0x00000020 /* Transmit completed OK */
+#define XEMACPS_TXSR_BUFEXH_MASK 0x00000010 /* Transmit buffs exhausted
+ mid frame */
+#define XEMACPS_TXSR_TXGO_MASK 0x00000008 /* Status of go flag */
+#define XEMACPS_TXSR_RXOVR_MASK 0x00000004 /* Retry limit exceeded */
+#define XEMACPS_TXSR_COL100_MASK 0x00000002 /* Collision 10/100 mode */
+#define XEMACPS_TXSR_USEDREAD_MASK 0x00000001 /* TX buffer used bit set */
+
+#define XEMACPS_TXSR_ERROR_MASK (XEMACPS_TXSR_HRESPNOK_MASK | \
+ XEMACPS_TXSR_COL1000_MASK | \
+ XEMACPS_TXSR_URUN_MASK | \
+ XEMACPS_TXSR_BUFEXH_MASK | \
+ XEMACPS_TXSR_RXOVR_MASK | \
+ XEMACPS_TXSR_COL100_MASK | \
+ XEMACPS_TXSR_USEDREAD_MASK)
+
+/* receive status register bit definitions */
+#define XEMACPS_RXSR_HRESPNOK_MASK 0x00000008 /* Receive hresp not OK */
+#define XEMACPS_RXSR_RXOVR_MASK 0x00000004 /* Receive overrun */
+#define XEMACPS_RXSR_FRAMERX_MASK 0x00000002 /* Frame received OK */
+#define XEMACPS_RXSR_BUFFNA_MASK 0x00000001 /* RX buffer used bit set */
+
+#define XEMACPS_RXSR_ERROR_MASK (XEMACPS_RXSR_HRESPNOK_MASK | \
+ XEMACPS_RXSR_RXOVR_MASK | \
+ XEMACPS_RXSR_BUFFNA_MASK)
+
+/* interrupts bit definitions
+ * Bits definitions are same in XEMACPS_ISR_OFFSET,
+ * XEMACPS_IER_OFFSET, XEMACPS_IDR_OFFSET, and XEMACPS_IMR_OFFSET
+ */
+#define XEMACPS_IXR_PTPPSTX_MASK 0x02000000 /* PTP Psync transmitted */
+#define XEMACPS_IXR_PTPPDRTX_MASK 0x01000000 /* PTP Pdelay_req
+ transmitted */
+#define XEMACPS_IXR_PTPSTX_MASK 0x00800000 /* PTP Sync transmitted */
+#define XEMACPS_IXR_PTPDRTX_MASK 0x00400000 /* PTP Delay_req
+ transmitted */
+#define XEMACPS_IXR_PTPPSRX_MASK 0x00200000 /* PTP Psync received */
+#define XEMACPS_IXR_PTPPDRRX_MASK 0x00100000 /* PTP Pdelay_req
+ received */
+#define XEMACPS_IXR_PTPSRX_MASK 0x00080000 /* PTP Sync received */
+#define XEMACPS_IXR_PTPDRRX_MASK 0x00040000 /* PTP Delay_req received */
+#define XEMACPS_IXR_PAUSETX_MASK 0x00004000 /* Pause frame
+ transmitted */
+#define XEMACPS_IXR_PAUSEZERO_MASK 0x00002000 /* Pause time has reached
+ zero */
+#define XEMACPS_IXR_PAUSENZERO_MASK 0x00001000 /* Pause frame received */
+#define XEMACPS_IXR_HRESPNOK_MASK 0x00000800 /* hresp not ok */
+#define XEMACPS_IXR_RXOVR_MASK 0x00000400 /* Receive overrun
+ occurred */
+#define XEMACPS_IXR_TXCOMPL_MASK 0x00000080 /* Frame transmitted ok */
+#define XEMACPS_IXR_TXEXH_MASK 0x00000040 /* Transmit err occurred or
+ no buffers*/
+#define XEMACPS_IXR_RETRY_MASK 0x00000020 /* Retry limit exceeded */
+#define XEMACPS_IXR_URUN_MASK 0x00000010 /* Transmit underrun */
+#define XEMACPS_IXR_TXUSED_MASK 0x00000008 /* Tx buffer used bit read */
+#define XEMACPS_IXR_RXUSED_MASK 0x00000004 /* Rx buffer used bit read */
+#define XEMACPS_IXR_FRAMERX_MASK 0x00000002 /* Frame received ok */
+#define XEMACPS_IXR_MGMNT_MASK 0x00000001 /* PHY management complete */
+#define XEMACPS_IXR_ALL_MASK 0x03FC7FFE /* Everything except MDIO */
+
+#define XEMACPS_IXR_TX_ERR_MASK (XEMACPS_IXR_TXEXH_MASK | \
+ XEMACPS_IXR_RETRY_MASK | \
+ XEMACPS_IXR_URUN_MASK | \
+ XEMACPS_IXR_TXUSED_MASK)
+
+#define XEMACPS_IXR_RX_ERR_MASK (XEMACPS_IXR_HRESPNOK_MASK | \
+ XEMACPS_IXR_RXUSED_MASK | \
+ XEMACPS_IXR_RXOVR_MASK)
+/* PHY Maintenance bit definitions */
+#define XEMACPS_PHYMNTNC_OP_MASK 0x40020000 /* operation mask bits */
+#define XEMACPS_PHYMNTNC_OP_R_MASK 0x20000000 /* read operation */
+#define XEMACPS_PHYMNTNC_OP_W_MASK 0x10000000 /* write operation */
+#define XEMACPS_PHYMNTNC_ADDR_MASK 0x0F800000 /* Address bits */
+#define XEMACPS_PHYMNTNC_REG_MASK 0x007C0000 /* register bits */
+#define XEMACPS_PHYMNTNC_DATA_MASK 0x0000FFFF /* data bits */
+#define XEMACPS_PHYMNTNC_PHYAD_SHIFT_MASK 23 /* Shift bits for PHYAD */
+#define XEMACPS_PHYMNTNC_PHREG_SHIFT_MASK 18 /* Shift bits for PHREG */
+
+/* Wake on LAN bit definition */
+#define XEMACPS_WOL_MCAST_MASK 0x00080000
+#define XEMACPS_WOL_SPEREG1_MASK 0x00040000
+#define XEMACPS_WOL_ARP_MASK 0x00020000
+#define XEMACPS_WOL_MAGIC_MASK 0x00010000
+#define XEMACPS_WOL_ARP_ADDR_MASK 0x0000FFFF
+
+/* Buffer descriptor status words offset */
+#define XEMACPS_BD_ADDR_OFFSET 0x00000000 /**< word 0/addr of BDs */
+#define XEMACPS_BD_STAT_OFFSET 0x00000004 /**< word 1/status of BDs */
+
+/* Transmit buffer descriptor status words bit positions.
+ * Transmit buffer descriptor consists of two 32-bit registers,
+ * the first - word0 contains a 32-bit address pointing to the location of
+ * the transmit data.
+ * The following register - word1, consists of various information to
+ * control transmit process. After transmit, this is updated with status
+ * information, whether the frame was transmitted OK or why it had failed.
+ */
+#define XEMACPS_TXBUF_USED_MASK 0x80000000 /* Used bit. */
+#define XEMACPS_TXBUF_WRAP_MASK 0x40000000 /* Wrap bit, last
+ descriptor */
+#define XEMACPS_TXBUF_RETRY_MASK 0x20000000 /* Retry limit exceeded */
+#define XEMACPS_TXBUF_EXH_MASK 0x08000000 /* Buffers exhausted */
+#define XEMACPS_TXBUF_LAC_MASK 0x04000000 /* Late collision. */
+#define XEMACPS_TXBUF_NOCRC_MASK 0x00010000 /* No CRC */
+#define XEMACPS_TXBUF_LAST_MASK 0x00008000 /* Last buffer */
+#define XEMACPS_TXBUF_LEN_MASK 0x00003FFF /* Mask for length field */
+
+#define XEMACPS_TXBUF_ERR_MASK 0x3C000000 /* Mask for length field */
+
+/* Receive buffer descriptor status words bit positions.
+ * Receive buffer descriptor consists of two 32-bit registers,
+ * the first - word0 contains a 32-bit word aligned address pointing to the
+ * address of the buffer. The lower two bits make up the wrap bit indicating
+ * the last descriptor and the ownership bit to indicate it has been used.
+ * The following register - word1, contains status information regarding why
+ * the frame was received (the filter match condition) as well as other
+ * useful info.
+ */
+#define XEMACPS_RXBUF_BCAST_MASK 0x80000000 /* Broadcast frame */
+#define XEMACPS_RXBUF_MULTIHASH_MASK 0x40000000 /* Multicast hashed frame */
+#define XEMACPS_RXBUF_UNIHASH_MASK 0x20000000 /* Unicast hashed frame */
+#define XEMACPS_RXBUF_EXH_MASK 0x08000000 /* buffer exhausted */
+#define XEMACPS_RXBUF_AMATCH_MASK 0x06000000 /* Specific address
+ matched */
+#define XEMACPS_RXBUF_IDFOUND_MASK 0x01000000 /* Type ID matched */
+#define XEMACPS_RXBUF_IDMATCH_MASK 0x00C00000 /* ID matched mask */
+#define XEMACPS_RXBUF_VLAN_MASK 0x00200000 /* VLAN tagged */
+#define XEMACPS_RXBUF_PRI_MASK 0x00100000 /* Priority tagged */
+#define XEMACPS_RXBUF_VPRI_MASK 0x000E0000 /* Vlan priority */
+#define XEMACPS_RXBUF_CFI_MASK 0x00010000 /* CFI frame */
+#define XEMACPS_RXBUF_EOF_MASK 0x00008000 /* End of frame. */
+#define XEMACPS_RXBUF_SOF_MASK 0x00004000 /* Start of frame. */
+#define XEMACPS_RXBUF_LEN_MASK 0x00003FFF /* Mask for length field */
+
+#define XEMACPS_RXBUF_WRAP_MASK 0x00000002 /* Wrap bit, last BD */
+#define XEMACPS_RXBUF_NEW_MASK 0x00000001 /* Used bit.. */
+#define XEMACPS_RXBUF_ADD_MASK 0xFFFFFFFC /* Mask for address */
+
+#define XEAMCPS_GEN_PURPOSE_TIMER_LOAD 100 /* timeout value is msecs */
+
+#define XEMACPS_GMII2RGMII_FULLDPLX BMCR_FULLDPLX
+#define XEMACPS_GMII2RGMII_SPEED1000 BMCR_SPEED1000
+#define XEMACPS_GMII2RGMII_SPEED100 BMCR_SPEED100
+#define XEMACPS_GMII2RGMII_REG_NUM 0x10
+
+#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP
+#define NS_PER_SEC 1000000000ULL /* Nanoseconds per
+ second */
+#endif
+
+#define xemacps_read(base, reg) \
+ __raw_readl(((void __iomem *)(base)) + (reg))
+#define xemacps_write(base, reg, val) \
+ __raw_writel((val), ((void __iomem *)(base)) + (reg))
+
+struct ring_info {
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+ size_t len;
+};
+
+/* DMA buffer descriptor structure. Each BD is two words */
+struct xemacps_bd {
+ u32 addr;
+ u32 ctrl;
+};
+
+
+/* Our private device data. */
+struct net_local {
+ void __iomem *baseaddr;
+ struct clk *devclk;
+ struct clk *aperclk;
+ struct notifier_block clk_rate_change_nb;
+
+ struct device_node *phy_node;
+ struct device_node *gmii2rgmii_phy_node;
+ struct ring_info *tx_skb;
+ struct ring_info *rx_skb;
+
+ struct xemacps_bd *rx_bd;
+ struct xemacps_bd *tx_bd;
+
+ dma_addr_t rx_bd_dma; /* physical address */
+ dma_addr_t tx_bd_dma; /* physical address */
+
+ u32 tx_bd_ci;
+ u32 tx_bd_tail;
+ u32 rx_bd_ci;
+
+ u32 tx_bd_freecnt;
+
+ spinlock_t tx_lock;
+ spinlock_t rx_lock;
+ spinlock_t nwctrlreg_lock;
+
+ struct platform_device *pdev;
+ struct net_device *ndev; /* this device */
+ struct tasklet_struct tx_bdreclaim_tasklet;
+ struct workqueue_struct *txtimeout_handler_wq;
+ struct work_struct txtimeout_reinit;
+
+ struct napi_struct napi; /* napi information for device */
+ struct net_device_stats stats; /* Statistics for this device */
+
+ struct timer_list gen_purpose_timer; /* Used for stats update */
+
+ /* Manage internal timer for packet timestamping */
+ struct cyclecounter cycles;
+ struct timecounter clock;
+ struct hwtstamp_config hwtstamp_config;
+
+ struct mii_bus *mii_bus;
+ struct phy_device *phy_dev;
+ struct phy_device *gmii2rgmii_phy_dev;
+ phy_interface_t phy_interface;
+ unsigned int link;
+ unsigned int speed;
+ unsigned int duplex;
+ /* RX ip/tcp/udp checksum */
+ unsigned ip_summed;
+ unsigned int enetnum;
+ unsigned int lastrxfrmscntr;
+#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP
+ unsigned int ptpenetclk;
+#endif
+};
+#define to_net_local(_nb) container_of(_nb, struct net_local,\
+ clk_rate_change_nb)
+
+static struct net_device_ops netdev_ops;
+
+/**
+ * xemacps_mdio_read - Read current value of phy register indicated by
+ * phyreg.
+ * @bus: mdio bus
+ * @mii_id: mii id
+ * @phyreg: phy register to be read
+ *
+ * @return: value read from specified phy register.
+ *
+ * note: This is for 802.3 clause 22 phys access. For 802.3 clause 45 phys
+ * access, set bit 30 to be 1. e.g. change XEMACPS_PHYMNTNC_OP_MASK to
+ * 0x00020000.
+ */
+static int xemacps_mdio_read(struct mii_bus *bus, int mii_id, int phyreg)
+{
+ struct net_local *lp = bus->priv;
+ u32 regval;
+ int value;
+ volatile u32 ipisr;
+
+ regval = XEMACPS_PHYMNTNC_OP_MASK;
+ regval |= XEMACPS_PHYMNTNC_OP_R_MASK;
+ regval |= (mii_id << XEMACPS_PHYMNTNC_PHYAD_SHIFT_MASK);
+ regval |= (phyreg << XEMACPS_PHYMNTNC_PHREG_SHIFT_MASK);
+
+ xemacps_write(lp->baseaddr, XEMACPS_PHYMNTNC_OFFSET, regval);
+
+ /* wait for end of transfer */
+ do {
+ cpu_relax();
+ ipisr = xemacps_read(lp->baseaddr, XEMACPS_NWSR_OFFSET);
+ } while ((ipisr & XEMACPS_NWSR_MDIOIDLE_MASK) == 0);
+
+ value = xemacps_read(lp->baseaddr, XEMACPS_PHYMNTNC_OFFSET) &
+ XEMACPS_PHYMNTNC_DATA_MASK;
+
+ return value;
+}
+
+/**
+ * xemacps_mdio_write - Write passed in value to phy register indicated
+ * by phyreg.
+ * @bus: mdio bus
+ * @mii_id: mii id
+ * @phyreg: phy register to be configured.
+ * @value: value to be written to phy register.
+ * return 0. This API requires to be int type or compile warning generated
+ *
+ * note: This is for 802.3 clause 22 phys access. For 802.3 clause 45 phys
+ * access, set bit 30 to be 1. e.g. change XEMACPS_PHYMNTNC_OP_MASK to
+ * 0x00020000.
+ */
+static int xemacps_mdio_write(struct mii_bus *bus, int mii_id, int phyreg,
+ u16 value)
+{
+ struct net_local *lp = bus->priv;
+ u32 regval;
+ volatile u32 ipisr;
+
+ regval = XEMACPS_PHYMNTNC_OP_MASK;
+ regval |= XEMACPS_PHYMNTNC_OP_W_MASK;
+ regval |= (mii_id << XEMACPS_PHYMNTNC_PHYAD_SHIFT_MASK);
+ regval |= (phyreg << XEMACPS_PHYMNTNC_PHREG_SHIFT_MASK);
+ regval |= value;
+
+ xemacps_write(lp->baseaddr, XEMACPS_PHYMNTNC_OFFSET, regval);
+
+ /* wait for end of transfer */
+ do {
+ cpu_relax();
+ ipisr = xemacps_read(lp->baseaddr, XEMACPS_NWSR_OFFSET);
+ } while ((ipisr & XEMACPS_NWSR_MDIOIDLE_MASK) == 0);
+
+ return 0;
+}
+
+
+/**
+ * xemacps_mdio_reset - mdio reset. It seems to be required per open
+ * source documentation phy.txt. But there is no reset in this device.
+ * Provide function API for now.
+ * @bus: mdio bus
+ **/
+static int xemacps_mdio_reset(struct mii_bus *bus)
+{
+ return 0;
+}
+
+/**
+ * xemacps_set_freq() - Set a clock to a new frequency
+ * @clk Pointer to the clock to change
+ * @rate New frequency in Hz
+ * @dev Pointer to the struct device
+ */
+static void xemacps_set_freq(struct clk *clk, long rate, struct device *dev)
+{
+ rate = clk_round_rate(clk, rate);
+ if (rate < 0)
+ return;
+
+ dev_info(dev, "Set clk to %ld Hz\n", rate);
+ if (clk_set_rate(clk, rate))
+ dev_err(dev, "Setting new clock rate failed.\n");
+}
+
+/**
+ * xemacps_adjust_link - handles link status changes, such as speed,
+ * duplex, up/down, ...
+ * @ndev: network device
+ */
+static void xemacps_adjust_link(struct net_device *ndev)
+{
+ struct net_local *lp = netdev_priv(ndev);
+ struct phy_device *phydev = lp->phy_dev;
+ struct phy_device *gmii2rgmii_phydev = lp->gmii2rgmii_phy_dev;
+ int status_change = 0;
+ u32 regval;
+ u16 gmii2rgmii_reg = 0;
+
+ if (phydev->link) {
+ if ((lp->speed != phydev->speed) ||
+ (lp->duplex != phydev->duplex)) {
+ regval = xemacps_read(lp->baseaddr,
+ XEMACPS_NWCFG_OFFSET);
+ regval &= ~(XEMACPS_NWCFG_FDEN_MASK |
+ XEMACPS_NWCFG_1000_MASK |
+ XEMACPS_NWCFG_100_MASK);
+
+ if (phydev->duplex) {
+ regval |= XEMACPS_NWCFG_FDEN_MASK;
+ gmii2rgmii_reg |= XEMACPS_GMII2RGMII_FULLDPLX;
+ }
+
+ if (phydev->speed == SPEED_1000) {
+ regval |= XEMACPS_NWCFG_1000_MASK;
+ gmii2rgmii_reg |= XEMACPS_GMII2RGMII_SPEED1000;
+ xemacps_set_freq(lp->devclk, 125000000,
+ &lp->pdev->dev);
+ } else if (phydev->speed == SPEED_100) {
+ regval |= XEMACPS_NWCFG_100_MASK;
+ gmii2rgmii_reg |= XEMACPS_GMII2RGMII_SPEED100;
+ xemacps_set_freq(lp->devclk, 25000000,
+ &lp->pdev->dev);
+ } else if (phydev->speed == SPEED_10) {
+ xemacps_set_freq(lp->devclk, 2500000,
+ &lp->pdev->dev);
+ } else {
+ dev_err(&lp->pdev->dev,
+ "%s: unknown PHY speed %d\n",
+ __func__, phydev->speed);
+ return;
+ }
+
+ xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET,
+ regval);
+
+ if (gmii2rgmii_phydev != NULL) {
+ xemacps_mdio_write(lp->mii_bus,
+ gmii2rgmii_phydev->addr,
+ XEMACPS_GMII2RGMII_REG_NUM,
+ gmii2rgmii_reg);
+ }
+
+ lp->speed = phydev->speed;
+ lp->duplex = phydev->duplex;
+ status_change = 1;
+ }
+ }
+
+ if (phydev->link != lp->link) {
+ lp->link = phydev->link;
+ status_change = 1;
+ }
+
+ if (status_change) {
+ if (phydev->link)
+ dev_info(&lp->pdev->dev, "link up (%d/%s)\n",
+ phydev->speed,
+ DUPLEX_FULL == phydev->duplex ?
+ "FULL" : "HALF");
+ else
+ dev_info(&lp->pdev->dev, "link down\n");
+ }
+}
+
+static int xemacps_clk_notifier_cb(struct notifier_block *nb, unsigned long
+ event, void *data)
+{
+/*
+ struct clk_notifier_data *ndata = data;
+ struct net_local *nl = to_net_local(nb);
+*/
+
+ switch (event) {
+ case PRE_RATE_CHANGE:
+ /* if a rate change is announced we need to check whether we
+ * can maintain the current frequency by changing the clock
+ * dividers.
+ * I don't see how this can be done using the current fmwk!?
+ * For now we always allow the rate change. Otherwise we would
+ * even prevent ourself to change the rate.
+ */
+ return NOTIFY_OK;
+ case POST_RATE_CHANGE:
+ /* not sure this will work. actually i'm sure it does not. this
+ * callback is not allowed to call back into COMMON_CLK, what
+ * adjust_link() does...
+ */
+ /*xemacps_adjust_link(nl->ndev); would likely lock up kernel */
+ return NOTIFY_OK;
+ case ABORT_RATE_CHANGE:
+ default:
+ return NOTIFY_DONE;
+ }
+}
+
+/**
+ * xemacps_mii_probe - probe mii bus, find the right bus_id to register
+ * phy callback function.
+ * @ndev: network interface device structure
+ * return 0 on success, negative value if error
+ **/
+static int xemacps_mii_probe(struct net_device *ndev)
+{
+ struct net_local *lp = netdev_priv(ndev);
+ struct phy_device *phydev = NULL;
+
+ if (lp->phy_node) {
+ phydev = of_phy_connect(lp->ndev,
+ lp->phy_node,
+ &xemacps_adjust_link,
+ 0,
+ lp->phy_interface);
+ }
+ if (!phydev) {
+ dev_err(&lp->pdev->dev, "%s: no PHY found\n", ndev->name);
+ return -1;
+ }
+
+ dev_dbg(&lp->pdev->dev,
+ "GEM: phydev %p, phydev->phy_id 0x%x, phydev->addr 0x%x\n",
+ phydev, phydev->phy_id, phydev->addr);
+
+ phydev->supported &= (PHY_GBIT_FEATURES | SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause);
+ phydev->advertising = phydev->supported;
+
+ lp->link = 0;
+ lp->speed = 0;
+ lp->duplex = -1;
+ lp->phy_dev = phydev;
+
+ phy_start(lp->phy_dev);
+
+ dev_dbg(&lp->pdev->dev, "phy_addr 0x%x, phy_id 0x%08x\n",
+ lp->phy_dev->addr, lp->phy_dev->phy_id);
+
+ dev_dbg(&lp->pdev->dev, "attach [%s] phy driver\n",
+ lp->phy_dev->drv->name);
+
+ if (lp->gmii2rgmii_phy_node) {
+ phydev = of_phy_connect(lp->ndev,
+ lp->gmii2rgmii_phy_node,
+ NULL,
+ 0, 0);
+ if (!phydev) {
+ dev_err(&lp->pdev->dev,
+ "%s: no gmii to rgmii converter found\n",
+ ndev->name);
+ return -1;
+ }
+ lp->gmii2rgmii_phy_dev = phydev;
+ } else
+ lp->gmii2rgmii_phy_dev = NULL;
+
+ return 0;
+}
+
+/**
+ * xemacps_mii_init - Initialize and register mii bus to network device
+ * @lp: local device instance pointer
+ * return 0 on success, negative value if error
+ **/
+static int xemacps_mii_init(struct net_local *lp)
+{
+ int rc = -ENXIO, i;
+ struct resource res;
+ struct device_node *np = of_get_parent(lp->phy_node);
+ struct device_node *npp;
+
+ lp->mii_bus = mdiobus_alloc();
+ if (lp->mii_bus == NULL) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+
+ lp->mii_bus->name = "XEMACPS mii bus";
+ lp->mii_bus->read = &xemacps_mdio_read;
+ lp->mii_bus->write = &xemacps_mdio_write;
+ lp->mii_bus->reset = &xemacps_mdio_reset;
+ lp->mii_bus->priv = lp;
+ lp->mii_bus->parent = &lp->ndev->dev;
+
+ lp->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
+ if (!lp->mii_bus->irq) {
+ rc = -ENOMEM;
+ goto err_out_free_mdiobus;
+ }
+
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ lp->mii_bus->irq[i] = PHY_POLL;
+ npp = of_get_parent(np);
+ of_address_to_resource(npp, 0, &res);
+ snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "%.8llx",
+ (unsigned long long)res.start);
+ if (of_mdiobus_register(lp->mii_bus, np))
+ goto err_out_free_mdio_irq;
+
+ return 0;
+
+err_out_free_mdio_irq:
+ kfree(lp->mii_bus->irq);
+err_out_free_mdiobus:
+ mdiobus_free(lp->mii_bus);
+err_out:
+ return rc;
+}
+
+/**
+ * xemacps_update_hdaddr - Update device's MAC address when configured
+ * MAC address is not valid, reconfigure with a good one.
+ * @lp: local device instance pointer
+ **/
+static void xemacps_update_hwaddr(struct net_local *lp)
+{
+ u32 regvall;
+ u16 regvalh;
+ u8 addr[6];
+
+ regvall = xemacps_read(lp->baseaddr, XEMACPS_LADDR1L_OFFSET);
+ regvalh = xemacps_read(lp->baseaddr, XEMACPS_LADDR1H_OFFSET);
+ addr[0] = regvall & 0xFF;
+ addr[1] = (regvall >> 8) & 0xFF;
+ addr[2] = (regvall >> 16) & 0xFF;
+ addr[3] = (regvall >> 24) & 0xFF;
+ addr[4] = regvalh & 0xFF;
+ addr[5] = (regvalh >> 8) & 0xFF;
+
+ if (is_valid_ether_addr(addr)) {
+ memcpy(lp->ndev->dev_addr, addr, sizeof(addr));
+ } else {
+ dev_info(&lp->pdev->dev, "invalid address, use assigned\n");
+ random_ether_addr(lp->ndev->dev_addr);
+ dev_info(&lp->pdev->dev,
+ "MAC updated %02x:%02x:%02x:%02x:%02x:%02x\n",
+ lp->ndev->dev_addr[0], lp->ndev->dev_addr[1],
+ lp->ndev->dev_addr[2], lp->ndev->dev_addr[3],
+ lp->ndev->dev_addr[4], lp->ndev->dev_addr[5]);
+ }
+}
+
+/**
+ * xemacps_set_hwaddr - Set device's MAC address from ndev->dev_addr
+ * @lp: local device instance pointer
+ **/
+static void xemacps_set_hwaddr(struct net_local *lp)
+{
+ u32 regvall = 0;
+ u16 regvalh = 0;
+#ifdef __LITTLE_ENDIAN
+ regvall = cpu_to_le32(*((u32 *)lp->ndev->dev_addr));
+ regvalh = cpu_to_le16(*((u16 *)(lp->ndev->dev_addr + 4)));
+#endif
+#ifdef __BIG_ENDIAN
+ regvall = cpu_to_be32(*((u32 *)lp->ndev->dev_addr));
+ regvalh = cpu_to_be16(*((u16 *)(lp->ndev->dev_addr + 4)));
+#endif
+ /* LADDRXH has to be wriiten latter than LADDRXL to enable
+ * this address even if these 16 bits are zeros.
+ */
+ xemacps_write(lp->baseaddr, XEMACPS_LADDR1L_OFFSET, regvall);
+ xemacps_write(lp->baseaddr, XEMACPS_LADDR1H_OFFSET, regvalh);
+#ifdef DEBUG
+ regvall = xemacps_read(lp->baseaddr, XEMACPS_LADDR1L_OFFSET);
+ regvalh = xemacps_read(lp->baseaddr, XEMACPS_LADDR1H_OFFSET);
+ dev_dbg(&lp->pdev->dev,
+ "MAC 0x%08x, 0x%08x, %02x:%02x:%02x:%02x:%02x:%02x\n",
+ regvall, regvalh,
+ (regvall & 0xff), ((regvall >> 8) & 0xff),
+ ((regvall >> 16) & 0xff), (regvall >> 24),
+ (regvalh & 0xff), (regvalh >> 8));
+#endif
+}
+
+/**
+ * xemacps_reset_hw - Helper function to reset the underlying hardware.
+ * This is called when we get into such deep trouble that we don't know
+ * how to handle otherwise.
+ * @lp: local device instance pointer
+ */
+static void xemacps_reset_hw(struct net_local *lp)
+{
+ u32 regisr;
+ /* make sure we have the buffer for ourselves */
+ wmb();
+
+ /* Have a clean start */
+ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, 0);
+
+ /* Clear statistic counters */
+ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET,
+ XEMACPS_NWCTRL_STATCLR_MASK);
+
+ /* Clear TX and RX status */
+ xemacps_write(lp->baseaddr, XEMACPS_TXSR_OFFSET, ~0UL);
+ xemacps_write(lp->baseaddr, XEMACPS_RXSR_OFFSET, ~0UL);
+
+ /* Disable all interrupts */
+ xemacps_write(lp->baseaddr, XEMACPS_IDR_OFFSET, ~0UL);
+ synchronize_irq(lp->ndev->irq);
+ regisr = xemacps_read(lp->baseaddr, XEMACPS_ISR_OFFSET);
+ xemacps_write(lp->baseaddr, XEMACPS_ISR_OFFSET, regisr);
+}
+
+#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP
+
+/**
+ * xemacps_get_hwticks - get the current value of the GEM internal timer
+ * @lp: local device instance pointer
+ * return: nothing
+ **/
+static inline void
+xemacps_get_hwticks(struct net_local *lp, u64 *sec, u64 *nsec)
+{
+ do {
+ *nsec = xemacps_read(lp->baseaddr, XEMACPS_1588NS_OFFSET);
+ *sec = xemacps_read(lp->baseaddr, XEMACPS_1588S_OFFSET);
+ } while (*nsec > xemacps_read(lp->baseaddr, XEMACPS_1588NS_OFFSET));
+}
+
+/**
+ * xemacps_read_clock - read raw cycle counter (to be used by time counter)
+ */
+static cycle_t xemacps_read_clock(const struct cyclecounter *tc)
+{
+ struct net_local *lp =
+ container_of(tc, struct net_local, cycles);
+ u64 stamp;
+ u64 sec, nsec;
+
+ xemacps_get_hwticks(lp, &sec, &nsec);
+ stamp = (sec << 32) | nsec;
+
+ return stamp;
+}
+
+
+/**
+ * xemacps_systim_to_hwtstamp - convert system time value to hw timestamp
+ * @adapter: board private structure
+ * @shhwtstamps: timestamp structure to update
+ * @regval: unsigned 64bit system time value.
+ *
+ * We need to convert the system time value stored in the RX/TXSTMP registers
+ * into a hwtstamp which can be used by the upper level timestamping functions
+ */
+static void xemacps_systim_to_hwtstamp(struct net_local *lp,
+ struct skb_shared_hwtstamps *shhwtstamps,
+ u64 regval)
+{
+ u64 ns;
+
+ ns = timecounter_cyc2time(&lp->clock, regval);
+ timecompare_update(&lp->compare, ns);
+ memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
+ shhwtstamps->hwtstamp = ns_to_ktime(ns);
+ shhwtstamps->syststamp = timecompare_transform(&lp->compare, ns);
+}
+
+static void
+xemacps_rx_hwtstamp(struct net_local *lp,
+ struct sk_buff *skb, unsigned msg_type)
+{
+ u64 time64, sec, nsec;
+
+ if (!msg_type) {
+ /* PTP Event Frame packets */
+ sec = xemacps_read(lp->baseaddr, XEMACPS_PTPERXS_OFFSET);
+ nsec = xemacps_read(lp->baseaddr, XEMACPS_PTPERXNS_OFFSET);
+ } else {
+ /* PTP Peer Event Frame packets */
+ sec = xemacps_read(lp->baseaddr, XEMACPS_PTPPRXS_OFFSET);
+ nsec = xemacps_read(lp->baseaddr, XEMACPS_PTPPRXNS_OFFSET);
+ }
+ time64 = (sec << 32) | nsec;
+ xemacps_systim_to_hwtstamp(lp, skb_hwtstamps(skb), time64);
+}
+
+static void
+xemacps_tx_hwtstamp(struct net_local *lp,
+ struct sk_buff *skb, unsigned msg_type)
+{
+ u64 time64, sec, nsec;
+
+ if (!msg_type) {
+ /* PTP Event Frame packets */
+ sec = xemacps_read(lp->baseaddr, XEMACPS_PTPETXS_OFFSET);
+ nsec = xemacps_read(lp->baseaddr, XEMACPS_PTPETXNS_OFFSET);
+ } else {
+ /* PTP Peer Event Frame packets */
+ sec = xemacps_read(lp->baseaddr, XEMACPS_PTPPTXS_OFFSET);
+ nsec = xemacps_read(lp->baseaddr, XEMACPS_PTPPTXNS_OFFSET);
+ }
+
+ time64 = (sec << 32) | nsec;
+ xemacps_systim_to_hwtstamp(lp, skb_hwtstamps(skb), time64);
+ skb_tstamp_tx(skb, skb_hwtstamps(skb));
+}
+
+#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */
+
+/**
+ * xemacps_rx - process received packets when napi called
+ * @lp: local device instance pointer
+ * @budget: NAPI budget
+ * return: number of BDs processed
+ **/
+static int xemacps_rx(struct net_local *lp, int budget)
+{
+ struct xemacps_bd *cur_p;
+ u32 len;
+ struct sk_buff *skb;
+ struct sk_buff *new_skb;
+ u32 new_skb_baddr;
+ unsigned int numbdfree = 0;
+ u32 size = 0;
+ u32 packets = 0;
+ u32 regval;
+
+ cur_p = &lp->rx_bd[lp->rx_bd_ci];
+ regval = cur_p->addr;
+ rmb();
+ while (numbdfree < budget) {
+ if (!(regval & XEMACPS_RXBUF_NEW_MASK))
+ break;
+
+ new_skb = netdev_alloc_skb(lp->ndev, XEMACPS_RX_BUF_SIZE);
+ if (new_skb == NULL) {
+ dev_err(&lp->ndev->dev, "no memory for new sk_buff\n");
+ break;
+ }
+ /* Get dma handle of skb->data */
+ new_skb_baddr = (u32) dma_map_single(lp->ndev->dev.parent,
+ new_skb->data,
+ XEMACPS_RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+
+ /* the packet length */
+ len = cur_p->ctrl & XEMACPS_RXBUF_LEN_MASK;
+ rmb();
+ skb = lp->rx_skb[lp->rx_bd_ci].skb;
+ dma_unmap_single(lp->ndev->dev.parent,
+ lp->rx_skb[lp->rx_bd_ci].mapping,
+ lp->rx_skb[lp->rx_bd_ci].len,
+ DMA_FROM_DEVICE);
+
+ /* setup received skb and send it upstream */
+ skb_put(skb, len); /* Tell the skb how much data we got. */
+ skb->protocol = eth_type_trans(skb, lp->ndev);
+
+ skb->ip_summed = lp->ip_summed;
+
+#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP
+ if ((lp->hwtstamp_config.rx_filter == HWTSTAMP_FILTER_ALL) &&
+ (ntohs(skb->protocol) == 0x800)) {
+ unsigned ip_proto, dest_port, msg_type;
+
+ /* While the GEM can timestamp PTP packets, it does
+ * not mark the RX descriptor to identify them. This
+ * is entirely the wrong place to be parsing UDP
+ * headers, but some minimal effort must be made.
+ * NOTE: the below parsing of ip_proto and dest_port
+ * depend on the use of Ethernet_II encapsulation,
+ * IPv4 without any options.
+ */
+ ip_proto = *((u8 *)skb->mac_header + 14 + 9);
+ dest_port = ntohs(*(((u16 *)skb->mac_header) +
+ ((14 + 20 + 2)/2)));
+ msg_type = *((u8 *)skb->mac_header + 42);
+ if ((ip_proto == IPPROTO_UDP) &&
+ (dest_port == 0x13F)) {
+ /* Timestamp this packet */
+ xemacps_rx_hwtstamp(lp, skb, msg_type & 0x2);
+ }
+ }
+#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */
+ size += len;
+ packets++;
+ netif_receive_skb(skb);
+
+ cur_p->addr = (cur_p->addr & ~XEMACPS_RXBUF_ADD_MASK)
+ | (new_skb_baddr);
+ lp->rx_skb[lp->rx_bd_ci].skb = new_skb;
+ lp->rx_skb[lp->rx_bd_ci].mapping = new_skb_baddr;
+ lp->rx_skb[lp->rx_bd_ci].len = XEMACPS_RX_BUF_SIZE;
+
+ cur_p->ctrl = 0;
+ cur_p->addr &= (~XEMACPS_RXBUF_NEW_MASK);
+ wmb();
+
+ lp->rx_bd_ci++;
+ lp->rx_bd_ci = lp->rx_bd_ci % XEMACPS_RECV_BD_CNT;
+ cur_p = &lp->rx_bd[lp->rx_bd_ci];
+ regval = cur_p->addr;
+ rmb();
+ numbdfree++;
+ }
+ wmb();
+ lp->stats.rx_packets += packets;
+ lp->stats.rx_bytes += size;
+ return numbdfree;
+}
+
+/**
+ * xemacps_rx_poll - NAPI poll routine
+ * napi: pointer to napi struct
+ * budget:
+ **/
+static int xemacps_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct net_local *lp = container_of(napi, struct net_local, napi);
+ int work_done = 0;
+ u32 regval;
+
+ spin_lock(&lp->rx_lock);
+ while (1) {
+ regval = xemacps_read(lp->baseaddr, XEMACPS_RXSR_OFFSET);
+ xemacps_write(lp->baseaddr, XEMACPS_RXSR_OFFSET, regval);
+ if (regval & XEMACPS_RXSR_HRESPNOK_MASK)
+ dev_err(&lp->pdev->dev, "RX error 0x%x\n", regval);
+
+ work_done += xemacps_rx(lp, budget - work_done);
+ if (work_done >= budget)
+ break;
+
+ napi_complete(napi);
+ /* We disabled RX interrupts in interrupt service
+ * routine, now it is time to enable it back.
+ */
+ xemacps_write(lp->baseaddr,
+ XEMACPS_IER_OFFSET, XEMACPS_IXR_FRAMERX_MASK);
+
+ /* If a packet has come in between the last check of the BD
+ * list and unmasking the interrupts, we may have missed the
+ * interrupt, so reschedule here.
+ */
+ if ((lp->rx_bd[lp->rx_bd_ci].addr & XEMACPS_RXBUF_NEW_MASK)
+ && napi_reschedule(napi)) {
+ xemacps_write(lp->baseaddr,
+ XEMACPS_IDR_OFFSET, XEMACPS_IXR_FRAMERX_MASK);
+ continue;
+ }
+ break;
+ }
+ spin_unlock(&lp->rx_lock);
+ return work_done;
+}
+
+/**
+ * xemacps_tx_poll - tx bd reclaim tasklet handler
+ * @data: pointer to network interface device structure
+ **/
+static void xemacps_tx_poll(unsigned long data)
+{
+ struct net_device *ndev = (struct net_device *)data;
+ struct net_local *lp = netdev_priv(ndev);
+ u32 regval;
+ u32 len = 0;
+ unsigned int bdcount = 0;
+ unsigned int bdpartialcount = 0;
+ unsigned int sop = 0;
+ struct xemacps_bd *cur_p;
+ u32 cur_i;
+ u32 numbdstofree;
+ u32 numbdsinhw;
+ struct ring_info *rp;
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ spin_lock(&lp->tx_lock);
+ regval = xemacps_read(lp->baseaddr, XEMACPS_TXSR_OFFSET);
+ xemacps_write(lp->baseaddr, XEMACPS_TXSR_OFFSET, regval);
+ dev_dbg(&lp->pdev->dev, "TX status 0x%x\n", regval);
+ if (regval & (XEMACPS_TXSR_HRESPNOK_MASK | XEMACPS_TXSR_BUFEXH_MASK))
+ dev_err(&lp->pdev->dev, "TX error 0x%x\n", regval);
+
+ cur_i = lp->tx_bd_ci;
+ cur_p = &lp->tx_bd[cur_i];
+ numbdsinhw = XEMACPS_SEND_BD_CNT - lp->tx_bd_freecnt;
+ while (bdcount < numbdsinhw) {
+ if (sop == 0) {
+ if (cur_p->ctrl & XEMACPS_TXBUF_USED_MASK)
+ sop = 1;
+ else
+ break;
+ }
+
+ bdcount++;
+ bdpartialcount++;
+
+ /* hardware has processed this BD so check the "last" bit.
+ * If it is clear, then there are more BDs for the current
+ * packet. Keep a count of these partial packet BDs.
+ */
+ if (cur_p->ctrl & XEMACPS_TXBUF_LAST_MASK) {
+ sop = 0;
+ bdpartialcount = 0;
+ }
+
+ cur_i++;
+ cur_i = cur_i % XEMACPS_SEND_BD_CNT;
+ cur_p = &lp->tx_bd[cur_i];
+ }
+ numbdstofree = bdcount - bdpartialcount;
+ lp->tx_bd_freecnt += numbdstofree;
+ numbdsinhw -= numbdstofree;
+ if (!numbdstofree)
+ goto tx_poll_out;
+
+ cur_p = &lp->tx_bd[lp->tx_bd_ci];
+ while (numbdstofree) {
+ rp = &lp->tx_skb[lp->tx_bd_ci];
+ skb = rp->skb;
+
+ len += (cur_p->ctrl & XEMACPS_TXBUF_LEN_MASK);
+
+#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP
+ if ((lp->hwtstamp_config.tx_type == HWTSTAMP_TX_ON) &&
+ (ntohs(skb->protocol) == 0x800)) {
+ unsigned ip_proto, dest_port, msg_type;
+
+ skb_reset_mac_header(skb);
+
+ ip_proto = *((u8 *)skb->mac_header + 14 + 9);
+ dest_port = ntohs(*(((u16 *)skb->mac_header) +
+ ((14 + 20 + 2)/2)));
+ msg_type = *((u8 *)skb->mac_header + 42);
+ if ((ip_proto == IPPROTO_UDP) &&
+ (dest_port == 0x13F)) {
+ /* Timestamp this packet */
+ xemacps_tx_hwtstamp(lp, skb, msg_type & 0x2);
+ }
+ }
+#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */
+
+ dma_unmap_single(&lp->pdev->dev, rp->mapping, rp->len,
+ DMA_TO_DEVICE);
+ rp->skb = NULL;
+ dev_kfree_skb(skb);
+ /* log tx completed packets and bytes, errors logs
+ * are in other error counters.
+ */
+ if (cur_p->ctrl & XEMACPS_TXBUF_LAST_MASK) {
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes += len;
+ len = 0;
+ }
+
+ /* Set used bit, preserve wrap bit; clear everything else. */
+ cur_p->ctrl |= XEMACPS_TXBUF_USED_MASK;
+ cur_p->ctrl &= (XEMACPS_TXBUF_USED_MASK |
+ XEMACPS_TXBUF_WRAP_MASK);
+
+ lp->tx_bd_ci++;
+ lp->tx_bd_ci = lp->tx_bd_ci % XEMACPS_SEND_BD_CNT;
+ cur_p = &lp->tx_bd[lp->tx_bd_ci];
+ numbdstofree--;
+ }
+ wmb();
+
+ if (numbdsinhw) {
+ spin_lock_irqsave(&lp->nwctrlreg_lock, flags);
+ regval = xemacps_read(lp->baseaddr, XEMACPS_NWCTRL_OFFSET);
+ regval |= XEMACPS_NWCTRL_STARTTX_MASK;
+ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, regval);
+ spin_unlock_irqrestore(&lp->nwctrlreg_lock, flags);
+ }
+
+ netif_wake_queue(ndev);
+
+tx_poll_out:
+ spin_unlock(&lp->tx_lock);
+}
+
+/**
+ * xemacps_interrupt - interrupt main service routine
+ * @irq: interrupt number
+ * @dev_id: pointer to a network device structure
+ * return IRQ_HANDLED or IRQ_NONE
+ **/
+static irqreturn_t xemacps_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = dev_id;
+ struct net_local *lp = netdev_priv(ndev);
+ u32 regisr;
+ u32 regctrl;
+
+ regisr = xemacps_read(lp->baseaddr, XEMACPS_ISR_OFFSET);
+ if (unlikely(!regisr))
+ return IRQ_NONE;
+
+ xemacps_write(lp->baseaddr, XEMACPS_ISR_OFFSET, regisr);
+
+ while (regisr) {
+ if (regisr & (XEMACPS_IXR_TXCOMPL_MASK |
+ XEMACPS_IXR_TX_ERR_MASK)) {
+ tasklet_schedule(&lp->tx_bdreclaim_tasklet);
+ }
+
+ if (regisr & XEMACPS_IXR_RXUSED_MASK) {
+ spin_lock(&lp->nwctrlreg_lock);
+ regctrl = xemacps_read(lp->baseaddr,
+ XEMACPS_NWCTRL_OFFSET);
+ regctrl |= XEMACPS_NWCTRL_FLUSH_DPRAM_MASK;
+ xemacps_write(lp->baseaddr,
+ XEMACPS_NWCTRL_OFFSET, regctrl);
+ spin_unlock(&lp->nwctrlreg_lock);
+ }
+
+ if (regisr & XEMACPS_IXR_FRAMERX_MASK) {
+ xemacps_write(lp->baseaddr,
+ XEMACPS_IDR_OFFSET, XEMACPS_IXR_FRAMERX_MASK);
+ napi_schedule(&lp->napi);
+ }
+ regisr = xemacps_read(lp->baseaddr, XEMACPS_ISR_OFFSET);
+ xemacps_write(lp->baseaddr, XEMACPS_ISR_OFFSET, regisr);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* Free all packets presently in the descriptor rings. */
+static void xemacps_clean_rings(struct net_local *lp)
+{
+ int i;
+
+ for (i = 0; i < XEMACPS_RECV_BD_CNT; i++) {
+ if (lp->rx_skb && lp->rx_skb[i].skb) {
+ dma_unmap_single(lp->ndev->dev.parent,
+ lp->rx_skb[i].mapping,
+ lp->rx_skb[i].len,
+ DMA_FROM_DEVICE);
+
+ dev_kfree_skb(lp->rx_skb[i].skb);
+ lp->rx_skb[i].skb = NULL;
+ lp->rx_skb[i].mapping = 0;
+ }
+ }
+
+ for (i = 0; i < XEMACPS_SEND_BD_CNT; i++) {
+ if (lp->tx_skb && lp->tx_skb[i].skb) {
+ dma_unmap_single(lp->ndev->dev.parent,
+ lp->tx_skb[i].mapping,
+ lp->tx_skb[i].len,
+ DMA_TO_DEVICE);
+
+ dev_kfree_skb(lp->tx_skb[i].skb);
+ lp->tx_skb[i].skb = NULL;
+ lp->tx_skb[i].mapping = 0;
+ }
+ }
+}
+
+/**
+ * xemacps_descriptor_free - Free allocated TX and RX BDs
+ * @lp: local device instance pointer
+ **/
+static void xemacps_descriptor_free(struct net_local *lp)
+{
+ int size;
+
+ xemacps_clean_rings(lp);
+
+ /* kfree(NULL) is safe, no need to check here */
+ kfree(lp->tx_skb);
+ lp->tx_skb = NULL;
+ kfree(lp->rx_skb);
+ lp->rx_skb = NULL;
+
+ size = XEMACPS_RECV_BD_CNT * sizeof(struct xemacps_bd);
+ if (lp->rx_bd) {
+ dma_free_coherent(&lp->pdev->dev, size,
+ lp->rx_bd, lp->rx_bd_dma);
+ lp->rx_bd = NULL;
+ }
+
+ size = XEMACPS_SEND_BD_CNT * sizeof(struct xemacps_bd);
+ if (lp->tx_bd) {
+ dma_free_coherent(&lp->pdev->dev, size,
+ lp->tx_bd, lp->tx_bd_dma);
+ lp->tx_bd = NULL;
+ }
+}
+
+/**
+ * xemacps_descriptor_init - Allocate both TX and RX BDs
+ * @lp: local device instance pointer
+ * return 0 on success, negative value if error
+ **/
+static int xemacps_descriptor_init(struct net_local *lp)
+{
+ int size;
+ struct sk_buff *new_skb;
+ u32 new_skb_baddr;
+ u32 i;
+ struct xemacps_bd *cur_p;
+ u32 regval;
+
+ lp->tx_skb = NULL;
+ lp->rx_skb = NULL;
+ lp->rx_bd = NULL;
+ lp->tx_bd = NULL;
+
+ /* Reset the indexes which are used for accessing the BDs */
+ lp->tx_bd_ci = 0;
+ lp->tx_bd_tail = 0;
+ lp->rx_bd_ci = 0;
+
+ size = XEMACPS_SEND_BD_CNT * sizeof(struct ring_info);
+ lp->tx_skb = kzalloc(size, GFP_KERNEL);
+ if (!lp->tx_skb)
+ goto err_out;
+ size = XEMACPS_RECV_BD_CNT * sizeof(struct ring_info);
+ lp->rx_skb = kzalloc(size, GFP_KERNEL);
+ if (!lp->rx_skb)
+ goto err_out;
+
+ /* Set up RX buffer descriptors. */
+
+ size = XEMACPS_RECV_BD_CNT * sizeof(struct xemacps_bd);
+ lp->rx_bd = dma_alloc_coherent(&lp->pdev->dev, size,
+ &lp->rx_bd_dma, GFP_KERNEL);
+ if (!lp->rx_bd)
+ goto err_out;
+ dev_dbg(&lp->pdev->dev, "RX ring %d bytes at 0x%x mapped %p\n",
+ size, lp->rx_bd_dma, lp->rx_bd);
+
+ for (i = 0; i < XEMACPS_RECV_BD_CNT; i++) {
+ cur_p = &lp->rx_bd[i];
+
+ new_skb = netdev_alloc_skb(lp->ndev, XEMACPS_RX_BUF_SIZE);
+ if (new_skb == NULL) {
+ dev_err(&lp->ndev->dev, "alloc_skb error %d\n", i);
+ goto err_out;
+ }
+
+ /* Get dma handle of skb->data */
+ new_skb_baddr = (u32) dma_map_single(lp->ndev->dev.parent,
+ new_skb->data,
+ XEMACPS_RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+
+ /* set wrap bit for last BD */
+ regval = (new_skb_baddr & XEMACPS_RXBUF_ADD_MASK);
+ if (i == XEMACPS_RECV_BD_CNT - 1)
+ regval |= XEMACPS_RXBUF_WRAP_MASK;
+ cur_p->addr = regval;
+ cur_p->ctrl = 0;
+ wmb();
+
+ lp->rx_skb[i].skb = new_skb;
+ lp->rx_skb[i].mapping = new_skb_baddr;
+ lp->rx_skb[i].len = XEMACPS_RX_BUF_SIZE;
+ }
+
+ /* Set up TX buffer descriptors. */
+
+ size = XEMACPS_SEND_BD_CNT * sizeof(struct xemacps_bd);
+ lp->tx_bd = dma_alloc_coherent(&lp->pdev->dev, size,
+ &lp->tx_bd_dma, GFP_KERNEL);
+ if (!lp->tx_bd)
+ goto err_out;
+ dev_dbg(&lp->pdev->dev, "TX ring %d bytes at 0x%x mapped %p\n",
+ size, lp->tx_bd_dma, lp->tx_bd);
+
+ for (i = 0; i < XEMACPS_SEND_BD_CNT; i++) {
+ cur_p = &lp->tx_bd[i];
+ /* set wrap bit for last BD */
+ cur_p->addr = 0;
+ regval = XEMACPS_TXBUF_USED_MASK;
+ if (i == XEMACPS_SEND_BD_CNT - 1)
+ regval |= XEMACPS_TXBUF_WRAP_MASK;
+ cur_p->ctrl = regval;
+ }
+ wmb();
+
+ lp->tx_bd_freecnt = XEMACPS_SEND_BD_CNT;
+
+ dev_dbg(&lp->pdev->dev,
+ "lp->tx_bd %p lp->tx_bd_dma %p lp->tx_skb %p\n",
+ lp->tx_bd, (void *)lp->tx_bd_dma, lp->tx_skb);
+ dev_dbg(&lp->pdev->dev,
+ "lp->rx_bd %p lp->rx_bd_dma %p lp->rx_skb %p\n",
+ lp->rx_bd, (void *)lp->rx_bd_dma, lp->rx_skb);
+
+ return 0;
+
+err_out:
+ xemacps_descriptor_free(lp);
+ return -ENOMEM;
+}
+
+#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP
+/*
+ * Initialize the GEM Time Stamp Unit
+ */
+static void xemacps_init_tsu(struct net_local *lp)
+{
+
+ memset(&lp->cycles, 0, sizeof(lp->cycles));
+ lp->cycles.read = xemacps_read_clock;
+ lp->cycles.mask = CLOCKSOURCE_MASK(64);
+ lp->cycles.mult = 1;
+ lp->cycles.shift = 0;
+
+ /* Set registers so that rollover occurs soon to test this. */
+ xemacps_write(lp->baseaddr, XEMACPS_1588NS_OFFSET, 0x00000000);
+ xemacps_write(lp->baseaddr, XEMACPS_1588S_OFFSET, 0xFF800000);
+
+ /* program the timer increment register with the numer of nanoseconds
+ * per clock tick.
+ *
+ * Note: The value is calculated based on the current operating
+ * frequency 50MHz
+ */
+ xemacps_write(lp->baseaddr, XEMACPS_1588INC_OFFSET,
+ (NS_PER_SEC/lp->ptpenetclk));
+
+ timecounter_init(&lp->clock, &lp->cycles,
+ ktime_to_ns(ktime_get_real()));
+ /*
+ * Synchronize our NIC clock against system wall clock.
+ */
+ memset(&lp->compare, 0, sizeof(lp->compare));
+ lp->compare.source = &lp->clock;
+ lp->compare.target = ktime_get_real;
+ lp->compare.num_samples = 10;
+ timecompare_update(&lp->compare, 0);
+
+ /* Initialize hwstamp config */
+ lp->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
+ lp->hwtstamp_config.tx_type = HWTSTAMP_TX_OFF;
+
+}
+#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */
+
+/**
+ * xemacps_init_hw - Initialize hardware to known good state
+ * @lp: local device instance pointer
+ **/
+static void xemacps_init_hw(struct net_local *lp)
+{
+ u32 regval;
+
+ xemacps_reset_hw(lp);
+ xemacps_set_hwaddr(lp);
+
+ /* network configuration */
+ regval = 0;
+ regval |= XEMACPS_NWCFG_FDEN_MASK;
+ regval |= XEMACPS_NWCFG_RXCHKSUMEN_MASK;
+ regval |= XEMACPS_NWCFG_PAUSECOPYDI_MASK;
+ regval |= XEMACPS_NWCFG_FCSREM_MASK;
+ regval |= XEMACPS_NWCFG_PAUSEEN_MASK;
+ regval |= XEMACPS_NWCFG_100_MASK;
+ regval |= XEMACPS_NWCFG_HDRXEN_MASK;
+
+ regval |= (MDC_DIV_224 << XEMACPS_NWCFG_MDC_SHIFT_MASK);
+ if (lp->ndev->flags & IFF_PROMISC) /* copy all */
+ regval |= XEMACPS_NWCFG_COPYALLEN_MASK;
+ if (!(lp->ndev->flags & IFF_BROADCAST)) /* No broadcast */
+ regval |= XEMACPS_NWCFG_BCASTDI_MASK;
+ xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET, regval);
+
+ /* Init TX and RX DMA Q address */
+ xemacps_write(lp->baseaddr, XEMACPS_RXQBASE_OFFSET, lp->rx_bd_dma);
+ xemacps_write(lp->baseaddr, XEMACPS_TXQBASE_OFFSET, lp->tx_bd_dma);
+
+ /* DMACR configurations */
+ regval = (((XEMACPS_RX_BUF_SIZE / XEMACPS_RX_BUF_UNIT) +
+ ((XEMACPS_RX_BUF_SIZE % XEMACPS_RX_BUF_UNIT) ? 1 : 0)) <<
+ XEMACPS_DMACR_RXBUF_SHIFT);
+ regval |= XEMACPS_DMACR_RXSIZE_MASK;
+ regval |= XEMACPS_DMACR_TXSIZE_MASK;
+ regval |= XEMACPS_DMACR_TCPCKSUM_MASK;
+#ifdef __LITTLE_ENDIAN
+ regval &= ~XEMACPS_DMACR_ENDIAN_MASK;
+#endif
+#ifdef __BIG_ENDIAN
+ regval |= XEMACPS_DMACR_ENDIAN_MASK;
+#endif
+ regval |= XEMACPS_DMACR_BLENGTH_INCR16;
+ xemacps_write(lp->baseaddr, XEMACPS_DMACR_OFFSET, regval);
+
+ /* Enable TX, RX and MDIO port */
+ regval = 0;
+ regval |= XEMACPS_NWCTRL_MDEN_MASK;
+ regval |= XEMACPS_NWCTRL_TXEN_MASK;
+ regval |= XEMACPS_NWCTRL_RXEN_MASK;
+ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, regval);
+
+#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP
+ /* Initialize the Time Stamp Unit */
+ xemacps_init_tsu(lp);
+#endif
+
+ /* Enable interrupts */
+ regval = XEMACPS_IXR_ALL_MASK;
+ xemacps_write(lp->baseaddr, XEMACPS_IER_OFFSET, regval);
+}
+
+/**
+ * xemacps_resetrx_for_no_rxdata - Resets the Rx if there is no data
+ * for a while (presently 100 msecs)
+ * @data: Used for net_local instance pointer
+ **/
+static void xemacps_resetrx_for_no_rxdata(unsigned long data)
+{
+ struct net_local *lp = (struct net_local *)data;
+ unsigned long regctrl;
+ unsigned long tempcntr;
+ unsigned long flags;
+
+ tempcntr = xemacps_read(lp->baseaddr, XEMACPS_RXCNT_OFFSET);
+ if ((!tempcntr) && (!(lp->lastrxfrmscntr))) {
+ spin_lock_irqsave(&lp->nwctrlreg_lock, flags);
+ regctrl = xemacps_read(lp->baseaddr,
+ XEMACPS_NWCTRL_OFFSET);
+ regctrl &= (~XEMACPS_NWCTRL_RXEN_MASK);
+ xemacps_write(lp->baseaddr,
+ XEMACPS_NWCTRL_OFFSET, regctrl);
+ regctrl = xemacps_read(lp->baseaddr, XEMACPS_NWCTRL_OFFSET);
+ regctrl |= (XEMACPS_NWCTRL_RXEN_MASK);
+ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, regctrl);
+ spin_unlock_irqrestore(&lp->nwctrlreg_lock, flags);
+ }
+ lp->lastrxfrmscntr = tempcntr;
+}
+
+/**
+ * xemacps_update_stats - Update the statistic structure entries from
+ * the corresponding emacps hardware statistic registers
+ * @data: Used for net_local instance pointer
+ **/
+static void xemacps_update_stats(unsigned long data)
+{
+ struct net_local *lp = (struct net_local *)data;
+ struct net_device_stats *nstat = &lp->stats;
+ u32 cnt;
+
+ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXUNDRCNT_OFFSET);
+ nstat->rx_errors += cnt;
+ nstat->rx_length_errors += cnt;
+
+ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXOVRCNT_OFFSET);
+ nstat->rx_errors += cnt;
+ nstat->rx_length_errors += cnt;
+
+ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXJABCNT_OFFSET);
+ nstat->rx_errors += cnt;
+ nstat->rx_length_errors += cnt;
+
+ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXFCSCNT_OFFSET);
+ nstat->rx_errors += cnt;
+ nstat->rx_crc_errors += cnt;
+
+ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXLENGTHCNT_OFFSET);
+ nstat->rx_errors += cnt;
+ nstat->rx_length_errors += cnt;
+
+ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXALIGNCNT_OFFSET);
+ nstat->rx_errors += cnt;
+ nstat->rx_frame_errors += cnt;
+
+ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXRESERRCNT_OFFSET);
+ nstat->rx_errors += cnt;
+ nstat->rx_missed_errors += cnt;
+
+ cnt = xemacps_read(lp->baseaddr, XEMACPS_RXORCNT_OFFSET);
+ nstat->rx_errors += cnt;
+ nstat->rx_fifo_errors += cnt;
+
+ cnt = xemacps_read(lp->baseaddr, XEMACPS_TXURUNCNT_OFFSET);
+ nstat->tx_errors += cnt;
+ nstat->tx_fifo_errors += cnt;
+
+ cnt = xemacps_read(lp->baseaddr, XEMACPS_SNGLCOLLCNT_OFFSET);
+ nstat->collisions += cnt;
+
+ cnt = xemacps_read(lp->baseaddr, XEMACPS_MULTICOLLCNT_OFFSET);
+ nstat->collisions += cnt;
+
+ cnt = xemacps_read(lp->baseaddr, XEMACPS_EXCESSCOLLCNT_OFFSET);
+ nstat->tx_errors += cnt;
+ nstat->tx_aborted_errors += cnt;
+ nstat->collisions += cnt;
+
+ cnt = xemacps_read(lp->baseaddr, XEMACPS_LATECOLLCNT_OFFSET);
+ nstat->tx_errors += cnt;
+ nstat->collisions += cnt;
+
+ cnt = xemacps_read(lp->baseaddr, XEMACPS_CSENSECNT_OFFSET);
+ nstat->tx_errors += cnt;
+ nstat->tx_carrier_errors += cnt;
+}
+
+/**
+ * xemacps_gen_purpose_timerhandler - Timer handler that is called at regular
+ * intervals upon expiry of the gen_purpose_timer defined in net_local struct.
+ * @data: Used for net_local instance pointer
+ *
+ * This timer handler is used to update the statistics by calling the API
+ * xemacps_update_stats. The statistics register can typically overflow pretty
+ * quickly under heavy load conditions. This timer is used to periodically
+ * read the stats registers and update the corresponding stats structure
+ * entries. The stats registers when read reset to 0.
+ **/
+static void xemacps_gen_purpose_timerhandler(unsigned long data)
+{
+ struct net_local *lp = (struct net_local *)data;
+
+ xemacps_update_stats(data);
+ xemacps_resetrx_for_no_rxdata(data);
+ mod_timer(&(lp->gen_purpose_timer),
+ jiffies + msecs_to_jiffies(XEAMCPS_GEN_PURPOSE_TIMER_LOAD));
+}
+
+/**
+ * xemacps_open - Called when a network device is made active
+ * @ndev: network interface device structure
+ * return 0 on success, negative value if error
+ *
+ * The open entry point is called when a network interface is made active
+ * by the system (IFF_UP). At this point all resources needed for transmit
+ * and receive operations are allocated, the interrupt handler is
+ * registered with OS, the watchdog timer is started, and the stack is
+ * notified that the interface is ready.
+ *
+ * note: if error(s), allocated resources before error require to be
+ * released or system issues (such as memory) leak might happen.
+ **/
+static int xemacps_open(struct net_device *ndev)
+{
+ struct net_local *lp = netdev_priv(ndev);
+ int rc;
+
+ dev_dbg(&lp->pdev->dev, "open\n");
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ rc = xemacps_descriptor_init(lp);
+ if (rc) {
+ dev_err(&lp->pdev->dev,
+ "Unable to allocate DMA memory, rc %d\n", rc);
+ return rc;
+ }
+
+ rc = pm_runtime_get_sync(&lp->pdev->dev);
+ if (rc < 0) {
+ dev_err(&lp->pdev->dev,
+ "pm_runtime_get_sync() failed, rc %d\n", rc);
+ goto err_free_rings;
+ }
+
+ xemacps_init_hw(lp);
+ rc = xemacps_mii_probe(ndev);
+ if (rc != 0) {
+ dev_err(&lp->pdev->dev,
+ "%s mii_probe fail.\n", lp->mii_bus->name);
+ if (rc == (-2)) {
+ mdiobus_unregister(lp->mii_bus);
+ kfree(lp->mii_bus->irq);
+ mdiobus_free(lp->mii_bus);
+ }
+ rc = -ENXIO;
+ goto err_pm_put;
+ }
+
+ setup_timer(&(lp->gen_purpose_timer), xemacps_gen_purpose_timerhandler,
+ (unsigned long)lp);
+ mod_timer(&(lp->gen_purpose_timer),
+ jiffies + msecs_to_jiffies(XEAMCPS_GEN_PURPOSE_TIMER_LOAD));
+
+ napi_enable(&lp->napi);
+ netif_carrier_on(ndev);
+ netif_start_queue(ndev);
+ tasklet_enable(&lp->tx_bdreclaim_tasklet);
+
+ return 0;
+
+err_pm_put:
+ xemacps_reset_hw(lp);
+ pm_runtime_put(&lp->pdev->dev);
+err_free_rings:
+ xemacps_descriptor_free(lp);
+
+ return rc;
+}
+
+/**
+ * xemacps_close - disable a network interface
+ * @ndev: network interface device structure
+ * return 0
+ *
+ * The close entry point is called when a network interface is de-activated
+ * by OS. The hardware is still under the driver control, but needs to be
+ * disabled. A global MAC reset is issued to stop the hardware, and all
+ * transmit and receive resources are freed.
+ **/
+static int xemacps_close(struct net_device *ndev)
+{
+ struct net_local *lp = netdev_priv(ndev);
+
+ del_timer_sync(&(lp->gen_purpose_timer));
+ netif_stop_queue(ndev);
+ napi_disable(&lp->napi);
+ tasklet_disable(&lp->tx_bdreclaim_tasklet);
+ netif_carrier_off(ndev);
+ if (lp->phy_dev)
+ phy_disconnect(lp->phy_dev);
+ if (lp->gmii2rgmii_phy_node)
+ phy_disconnect(lp->gmii2rgmii_phy_dev);
+ xemacps_reset_hw(lp);
+ mdelay(500);
+ xemacps_descriptor_free(lp);
+
+ pm_runtime_put(&lp->pdev->dev);
+
+ return 0;
+}
+
+/**
+ * xemacps_reinit_for_txtimeout - work queue scheduled for the tx timeout
+ * handling.
+ * @ndev: queue work structure
+ **/
+static void xemacps_reinit_for_txtimeout(struct work_struct *data)
+{
+ struct net_local *lp = container_of(data, struct net_local,
+ txtimeout_reinit);
+ int rc;
+
+ netif_stop_queue(lp->ndev);
+ napi_disable(&lp->napi);
+ tasklet_disable(&lp->tx_bdreclaim_tasklet);
+ spin_lock_bh(&lp->tx_lock);
+ xemacps_reset_hw(lp);
+ spin_unlock_bh(&lp->tx_lock);
+
+ if (lp->phy_dev)
+ phy_stop(lp->phy_dev);
+
+ xemacps_descriptor_free(lp);
+ rc = xemacps_descriptor_init(lp);
+ if (rc) {
+ dev_err(&lp->pdev->dev,
+ "Unable to allocate DMA memory, rc %d\n", rc);
+ return;
+ }
+
+ xemacps_init_hw(lp);
+
+ lp->link = 0;
+ lp->speed = 0;
+ lp->duplex = -1;
+
+ if (lp->phy_dev)
+ phy_start(lp->phy_dev);
+
+ napi_enable(&lp->napi);
+ tasklet_enable(&lp->tx_bdreclaim_tasklet);
+ lp->ndev->trans_start = jiffies;
+ netif_wake_queue(lp->ndev);
+}
+
+/**
+ * xemacps_tx_timeout - callback used when the transmitter has not made
+ * any progress for dev->watchdog ticks.
+ * @ndev: network interface device structure
+ **/
+static void xemacps_tx_timeout(struct net_device *ndev)
+{
+ struct net_local *lp = netdev_priv(ndev);
+
+ dev_err(&lp->pdev->dev, "transmit timeout %lu ms, reseting...\n",
+ TX_TIMEOUT * 1000UL / HZ);
+ queue_work(lp->txtimeout_handler_wq, &lp->txtimeout_reinit);
+}
+
+/**
+ * xemacps_set_mac_address - set network interface mac address
+ * @ndev: network interface device structure
+ * @addr: pointer to MAC address
+ * return 0 on success, negative value if error
+ **/
+static int xemacps_set_mac_address(struct net_device *ndev, void *addr)
+{
+ struct net_local *lp = netdev_priv(ndev);
+ struct sockaddr *hwaddr = (struct sockaddr *)addr;
+
+ if (netif_running(ndev))
+ return -EBUSY;
+
+ if (!is_valid_ether_addr(hwaddr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ dev_dbg(&lp->pdev->dev, "hwaddr 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ hwaddr->sa_data[0], hwaddr->sa_data[1], hwaddr->sa_data[2],
+ hwaddr->sa_data[3], hwaddr->sa_data[4], hwaddr->sa_data[5]);
+
+ memcpy(ndev->dev_addr, hwaddr->sa_data, ndev->addr_len);
+
+ xemacps_set_hwaddr(lp);
+ return 0;
+}
+
+/**
+ * xemacps_clear_csum - Clear the csum field for transport protocols
+ * @skb: socket buffer
+ * @ndev: network interface device structure
+ * return 0 on success, other value if error
+ **/
+static int xemacps_clear_csum(struct sk_buff *skb, struct net_device *ndev)
+{
+ /* Only run for packets requiring a checksum. */
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ return 0;
+
+ if (unlikely(skb_cow_head(skb, 0)))
+ return -1;
+
+ *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0;
+
+ return 0;
+}
+
+/**
+ * xemacps_start_xmit - transmit a packet (called by kernel)
+ * @skb: socket buffer
+ * @ndev: network interface device structure
+ * return 0 on success, other value if error
+ **/
+static int xemacps_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct net_local *lp = netdev_priv(ndev);
+ dma_addr_t mapping;
+ unsigned int nr_frags, len;
+ int i;
+ u32 regval;
+ void *virt_addr;
+ skb_frag_t *frag;
+ struct xemacps_bd *cur_p;
+ unsigned long flags;
+ u32 bd_tail;
+
+ nr_frags = skb_shinfo(skb)->nr_frags + 1;
+ spin_lock_bh(&lp->tx_lock);
+
+ if (nr_frags > lp->tx_bd_freecnt) {
+ netif_stop_queue(ndev); /* stop send queue */
+ spin_unlock_bh(&lp->tx_lock);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (xemacps_clear_csum(skb, ndev)) {
+ spin_unlock_bh(&lp->tx_lock);
+ kfree(skb);
+ return NETDEV_TX_OK;
+ }
+
+ bd_tail = lp->tx_bd_tail;
+ cur_p = &lp->tx_bd[bd_tail];
+ lp->tx_bd_freecnt -= nr_frags;
+ frag = &skb_shinfo(skb)->frags[0];
+
+ for (i = 0; i < nr_frags; i++) {
+ if (i == 0) {
+ len = skb_headlen(skb);
+ mapping = dma_map_single(&lp->pdev->dev, skb->data,
+ len, DMA_TO_DEVICE);
+ } else {
+ len = skb_frag_size(frag);
+ virt_addr = skb_frag_address(frag);
+ mapping = dma_map_single(&lp->pdev->dev, virt_addr,
+ len, DMA_TO_DEVICE);
+ frag++;
+ skb_get(skb);
+ }
+
+ lp->tx_skb[lp->tx_bd_tail].skb = skb;
+ lp->tx_skb[lp->tx_bd_tail].mapping = mapping;
+ lp->tx_skb[lp->tx_bd_tail].len = len;
+ cur_p->addr = mapping;
+
+ /* preserve critical status bits */
+ regval = cur_p->ctrl;
+ regval &= (XEMACPS_TXBUF_USED_MASK | XEMACPS_TXBUF_WRAP_MASK);
+ /* update length field */
+ regval |= ((regval & ~XEMACPS_TXBUF_LEN_MASK) | len);
+ /* commit second to last buffer to hardware */
+ if (i != 0)
+ regval &= ~XEMACPS_TXBUF_USED_MASK;
+ /* last fragment of this packet? */
+ if (i == (nr_frags - 1))
+ regval |= XEMACPS_TXBUF_LAST_MASK;
+ cur_p->ctrl = regval;
+
+ lp->tx_bd_tail++;
+ lp->tx_bd_tail = lp->tx_bd_tail % XEMACPS_SEND_BD_CNT;
+ cur_p = &(lp->tx_bd[lp->tx_bd_tail]);
+ }
+ wmb();
+
+ /* commit first buffer to hardware -- do this after
+ * committing the other buffers to avoid an underrun */
+ cur_p = &lp->tx_bd[bd_tail];
+ regval = cur_p->ctrl;
+ regval &= ~XEMACPS_TXBUF_USED_MASK;
+ cur_p->ctrl = regval;
+ wmb();
+
+ spin_lock_irqsave(&lp->nwctrlreg_lock, flags);
+ regval = xemacps_read(lp->baseaddr, XEMACPS_NWCTRL_OFFSET);
+ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET,
+ (regval | XEMACPS_NWCTRL_STARTTX_MASK));
+ spin_unlock_irqrestore(&lp->nwctrlreg_lock, flags);
+
+ spin_unlock_bh(&lp->tx_lock);
+ ndev->trans_start = jiffies;
+ return 0;
+}
+
+/* Get the MAC Address bit from the specified position */
+static unsigned get_bit(u8 *mac, unsigned bit)
+{
+ unsigned byte;
+
+ byte = mac[bit / 8];
+ byte >>= (bit & 0x7);
+ byte &= 1;
+
+ return byte;
+}
+
+/* Calculate a GEM MAC Address hash index */
+static unsigned calc_mac_hash(u8 *mac)
+{
+ int index_bit, mac_bit;
+ unsigned hash_index;
+
+ hash_index = 0;
+ mac_bit = 5;
+ for (index_bit = 5; index_bit >= 0; index_bit--) {
+ hash_index |= (get_bit(mac, mac_bit) ^
+ get_bit(mac, mac_bit + 6) ^
+ get_bit(mac, mac_bit + 12) ^
+ get_bit(mac, mac_bit + 18) ^
+ get_bit(mac, mac_bit + 24) ^
+ get_bit(mac, mac_bit + 30) ^
+ get_bit(mac, mac_bit + 36) ^
+ get_bit(mac, mac_bit + 42))
+ << index_bit;
+ mac_bit--;
+ }
+
+ return hash_index;
+}
+
+/**
+ * xemacps_set_hashtable - Add multicast addresses to the internal
+ * multicast-hash table. Called from xemac_set_rx_mode().
+ * @ndev: network interface device structure
+ *
+ * The hash address register is 64 bits long and takes up two
+ * locations in the memory map. The least significant bits are stored
+ * in EMAC_HSL and the most significant bits in EMAC_HSH.
+ *
+ * The unicast hash enable and the multicast hash enable bits in the
+ * network configuration register enable the reception of hash matched
+ * frames. The destination address is reduced to a 6 bit index into
+ * the 64 bit hash register using the following hash function. The
+ * hash function is an exclusive or of every sixth bit of the
+ * destination address.
+ *
+ * hi[5] = da[5] ^ da[11] ^ da[17] ^ da[23] ^ da[29] ^ da[35] ^ da[41] ^ da[47]
+ * hi[4] = da[4] ^ da[10] ^ da[16] ^ da[22] ^ da[28] ^ da[34] ^ da[40] ^ da[46]
+ * hi[3] = da[3] ^ da[09] ^ da[15] ^ da[21] ^ da[27] ^ da[33] ^ da[39] ^ da[45]
+ * hi[2] = da[2] ^ da[08] ^ da[14] ^ da[20] ^ da[26] ^ da[32] ^ da[38] ^ da[44]
+ * hi[1] = da[1] ^ da[07] ^ da[13] ^ da[19] ^ da[25] ^ da[31] ^ da[37] ^ da[43]
+ * hi[0] = da[0] ^ da[06] ^ da[12] ^ da[18] ^ da[24] ^ da[30] ^ da[36] ^ da[42]
+ *
+ * da[0] represents the least significant bit of the first byte
+ * received, that is, the multicast/unicast indicator, and da[47]
+ * represents the most significant bit of the last byte received. If
+ * the hash index, hi[n], points to a bit that is set in the hash
+ * register then the frame will be matched according to whether the
+ * frame is multicast or unicast. A multicast match will be signalled
+ * if the multicast hash enable bit is set, da[0] is 1 and the hash
+ * index points to a bit set in the hash register. A unicast match
+ * will be signalled if the unicast hash enable bit is set, da[0] is 0
+ * and the hash index points to a bit set in the hash register. To
+ * receive all multicast frames, the hash register should be set with
+ * all ones and the multicast hash enable bit should be set in the
+ * network configuration register.
+ **/
+static void xemacps_set_hashtable(struct net_device *ndev)
+{
+ struct netdev_hw_addr *curr;
+ u32 regvalh, regvall, hash_index;
+ u8 *mc_addr;
+ struct net_local *lp;
+
+ lp = netdev_priv(ndev);
+
+ regvalh = regvall = 0;
+
+ netdev_for_each_mc_addr(curr, ndev) {
+ if (!curr) /* end of list */
+ break;
+ mc_addr = curr->addr;
+ hash_index = calc_mac_hash(mc_addr);
+
+ if (hash_index >= XEMACPS_MAX_HASH_BITS) {
+ dev_err(&lp->pdev->dev,
+ "hash calculation out of range %d\n",
+ hash_index);
+ break;
+ }
+ if (hash_index < 32)
+ regvall |= (1 << hash_index);
+ else
+ regvalh |= (1 << (hash_index - 32));
+ }
+
+ xemacps_write(lp->baseaddr, XEMACPS_HASHL_OFFSET, regvall);
+ xemacps_write(lp->baseaddr, XEMACPS_HASHH_OFFSET, regvalh);
+}
+
+/**
+ * xemacps_set_rx_mode - enable/disable promiscuous and multicast modes
+ * @ndev: network interface device structure
+ **/
+static void xemacps_set_rx_mode(struct net_device *ndev)
+{
+ struct net_local *lp = netdev_priv(ndev);
+ u32 regval;
+
+ regval = xemacps_read(lp->baseaddr, XEMACPS_NWCFG_OFFSET);
+
+ /* promisc mode */
+ if (ndev->flags & IFF_PROMISC)
+ regval |= XEMACPS_NWCFG_COPYALLEN_MASK;
+ if (!(ndev->flags & IFF_PROMISC))
+ regval &= ~XEMACPS_NWCFG_COPYALLEN_MASK;
+
+ /* All multicast mode */
+ if (ndev->flags & IFF_ALLMULTI) {
+ regval |= XEMACPS_NWCFG_MCASTHASHEN_MASK;
+ xemacps_write(lp->baseaddr, XEMACPS_HASHL_OFFSET, ~0UL);
+ xemacps_write(lp->baseaddr, XEMACPS_HASHH_OFFSET, ~0UL);
+ /* Specific multicast mode */
+ } else if ((ndev->flags & IFF_MULTICAST)
+ && (netdev_mc_count(ndev) > 0)) {
+ regval |= XEMACPS_NWCFG_MCASTHASHEN_MASK;
+ xemacps_set_hashtable(ndev);
+ /* Disable multicast mode */
+ } else {
+ xemacps_write(lp->baseaddr, XEMACPS_HASHL_OFFSET, 0x0);
+ xemacps_write(lp->baseaddr, XEMACPS_HASHH_OFFSET, 0x0);
+ regval &= ~XEMACPS_NWCFG_MCASTHASHEN_MASK;
+ }
+
+ /* broadcast mode */
+ if (ndev->flags & IFF_BROADCAST)
+ regval &= ~XEMACPS_NWCFG_BCASTDI_MASK;
+ /* No broadcast */
+ if (!(ndev->flags & IFF_BROADCAST))
+ regval |= XEMACPS_NWCFG_BCASTDI_MASK;
+
+ xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET, regval);
+}
+
+#define MIN_MTU 60
+#define MAX_MTU 1500
+/**
+ * xemacps_change_mtu - Change maximum transfer unit
+ * @ndev: network interface device structure
+ * @new_mtu: new vlaue for maximum frame size
+ * return: 0 on success, negative value if error.
+ **/
+static int xemacps_change_mtu(struct net_device *ndev, int new_mtu)
+{
+ if ((new_mtu < MIN_MTU) ||
+ ((new_mtu + ndev->hard_header_len) > MAX_MTU))
+ return -EINVAL;
+
+ ndev->mtu = new_mtu; /* change mtu in net_device structure */
+ return 0;
+}
+
+/**
+ * xemacps_get_settings - get device specific settings.
+ * Usage: Issue "ethtool ethX" under linux prompt.
+ * @ndev: network device
+ * @ecmd: ethtool command structure
+ * return: 0 on success, negative value if error.
+ **/
+static int
+xemacps_get_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
+{
+ struct net_local *lp = netdev_priv(ndev);
+ struct phy_device *phydev = lp->phy_dev;
+
+ if (!phydev)
+ return -ENODEV;
+
+ return phy_ethtool_gset(phydev, ecmd);
+}
+
+/**
+ * xemacps_set_settings - set device specific settings.
+ * Usage: Issue "ethtool -s ethX speed 1000" under linux prompt
+ * to change speed
+ * @ndev: network device
+ * @ecmd: ethtool command structure
+ * return: 0 on success, negative value if error.
+ **/
+static int
+xemacps_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
+{
+ struct net_local *lp = netdev_priv(ndev);
+ struct phy_device *phydev = lp->phy_dev;
+
+ if (!phydev)
+ return -ENODEV;
+
+ return phy_ethtool_sset(phydev, ecmd);
+}
+
+/**
+ * xemacps_get_drvinfo - report driver information
+ * Usage: Issue "ethtool -i ethX" under linux prompt
+ * @ndev: network device
+ * @ed: device driver information structure
+ **/
+static void
+xemacps_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *ed)
+{
+ struct net_local *lp = netdev_priv(ndev);
+
+ memset(ed, 0, sizeof(struct ethtool_drvinfo));
+ strcpy(ed->driver, lp->pdev->dev.driver->name);
+ strcpy(ed->version, DRIVER_VERSION);
+}
+
+/**
+ * xemacps_get_ringparam - get device dma ring information.
+ * Usage: Issue "ethtool -g ethX" under linux prompt
+ * @ndev: network device
+ * @erp: ethtool ring parameter structure
+ **/
+static void
+xemacps_get_ringparam(struct net_device *ndev, struct ethtool_ringparam *erp)
+{
+ memset(erp, 0, sizeof(struct ethtool_ringparam));
+
+ erp->rx_max_pending = XEMACPS_RECV_BD_CNT;
+ erp->tx_max_pending = XEMACPS_SEND_BD_CNT;
+ erp->rx_pending = 0;
+ erp->tx_pending = 0;
+}
+
+/**
+ * xemacps_get_wol - get device wake on lan status
+ * Usage: Issue "ethtool ethX" under linux prompt
+ * @ndev: network device
+ * @ewol: wol status
+ **/
+static void
+xemacps_get_wol(struct net_device *ndev, struct ethtool_wolinfo *ewol)
+{
+ struct net_local *lp = netdev_priv(ndev);
+ u32 regval;
+
+ ewol->supported = WAKE_MAGIC | WAKE_ARP | WAKE_UCAST | WAKE_MCAST;
+
+ regval = xemacps_read(lp->baseaddr, XEMACPS_WOL_OFFSET);
+ if (regval | XEMACPS_WOL_MCAST_MASK)
+ ewol->wolopts |= WAKE_MCAST;
+ if (regval | XEMACPS_WOL_ARP_MASK)
+ ewol->wolopts |= WAKE_ARP;
+ if (regval | XEMACPS_WOL_SPEREG1_MASK)
+ ewol->wolopts |= WAKE_UCAST;
+ if (regval | XEMACPS_WOL_MAGIC_MASK)
+ ewol->wolopts |= WAKE_MAGIC;
+
+}
+
+/**
+ * xemacps_set_wol - set device wake on lan configuration
+ * Usage: Issue "ethtool -s ethX wol u|m|b|g" under linux prompt to enable
+ * specified type of packet.
+ * Usage: Issue "ethtool -s ethX wol d" under linux prompt to disable
+ * this feature.
+ * @ndev: network device
+ * @ewol: wol status
+ * return 0 on success, negative value if not supported
+ **/
+static int
+xemacps_set_wol(struct net_device *ndev, struct ethtool_wolinfo *ewol)
+{
+ struct net_local *lp = netdev_priv(ndev);
+ u32 regval;
+
+ if (ewol->wolopts & ~(WAKE_MAGIC | WAKE_ARP | WAKE_UCAST | WAKE_MCAST))
+ return -EOPNOTSUPP;
+
+ regval = xemacps_read(lp->baseaddr, XEMACPS_WOL_OFFSET);
+ regval &= ~(XEMACPS_WOL_MCAST_MASK | XEMACPS_WOL_ARP_MASK |
+ XEMACPS_WOL_SPEREG1_MASK | XEMACPS_WOL_MAGIC_MASK);
+
+ if (ewol->wolopts & WAKE_MAGIC)
+ regval |= XEMACPS_WOL_MAGIC_MASK;
+ if (ewol->wolopts & WAKE_ARP)
+ regval |= XEMACPS_WOL_ARP_MASK;
+ if (ewol->wolopts & WAKE_UCAST)
+ regval |= XEMACPS_WOL_SPEREG1_MASK;
+ if (ewol->wolopts & WAKE_MCAST)
+ regval |= XEMACPS_WOL_MCAST_MASK;
+
+ xemacps_write(lp->baseaddr, XEMACPS_WOL_OFFSET, regval);
+
+ return 0;
+}
+
+/**
+ * xemacps_get_pauseparam - get device pause status
+ * Usage: Issue "ethtool -a ethX" under linux prompt
+ * @ndev: network device
+ * @epauseparam: pause parameter
+ *
+ * note: hardware supports only tx flow control
+ **/
+static void
+xemacps_get_pauseparam(struct net_device *ndev,
+ struct ethtool_pauseparam *epauseparm)
+{
+ struct net_local *lp = netdev_priv(ndev);
+ u32 regval;
+
+ epauseparm->autoneg = 0;
+ epauseparm->rx_pause = 0;
+
+ regval = xemacps_read(lp->baseaddr, XEMACPS_NWCFG_OFFSET);
+ epauseparm->tx_pause = regval & XEMACPS_NWCFG_PAUSEEN_MASK;
+}
+
+/**
+ * xemacps_set_pauseparam - set device pause parameter(flow control)
+ * Usage: Issue "ethtool -A ethX tx on|off" under linux prompt
+ * @ndev: network device
+ * @epauseparam: pause parameter
+ * return 0 on success, negative value if not supported
+ *
+ * note: hardware supports only tx flow control
+ **/
+static int
+xemacps_set_pauseparam(struct net_device *ndev,
+ struct ethtool_pauseparam *epauseparm)
+{
+ struct net_local *lp = netdev_priv(ndev);
+ u32 regval;
+
+ if (netif_running(ndev)) {
+ dev_err(&lp->pdev->dev,
+ "Please stop netif before apply configruation\n");
+ return -EFAULT;
+ }
+
+ regval = xemacps_read(lp->baseaddr, XEMACPS_NWCFG_OFFSET);
+
+ if (epauseparm->tx_pause)
+ regval |= XEMACPS_NWCFG_PAUSEEN_MASK;
+ if (!(epauseparm->tx_pause))
+ regval &= ~XEMACPS_NWCFG_PAUSEEN_MASK;
+
+ xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET, regval);
+ return 0;
+}
+
+/**
+ * xemacps_get_stats - get device statistic raw data in 64bit mode
+ * @ndev: network device
+ **/
+static struct net_device_stats
+*xemacps_get_stats(struct net_device *ndev)
+{
+ struct net_local *lp = netdev_priv(ndev);
+ struct net_device_stats *nstat = &lp->stats;
+
+ xemacps_update_stats((unsigned long)lp);
+ return nstat;
+}
+
+static struct ethtool_ops xemacps_ethtool_ops = {
+ .get_settings = xemacps_get_settings,
+ .set_settings = xemacps_set_settings,
+ .get_drvinfo = xemacps_get_drvinfo,
+ .get_link = ethtool_op_get_link, /* ethtool default */
+ .get_ringparam = xemacps_get_ringparam,
+ .get_wol = xemacps_get_wol,
+ .set_wol = xemacps_set_wol,
+ .get_pauseparam = xemacps_get_pauseparam,
+ .set_pauseparam = xemacps_set_pauseparam,
+};
+
+#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP
+static int xemacps_hwtstamp_ioctl(struct net_device *netdev,
+ struct ifreq *ifr, int cmd)
+{
+ struct hwtstamp_config config;
+ struct net_local *lp;
+ u32 regval;
+
+ lp = netdev_priv(netdev);
+
+ if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+ return -EFAULT;
+
+ /* reserved for future extensions */
+ if (config.flags)
+ return -EINVAL;
+
+ if ((config.tx_type != HWTSTAMP_TX_OFF) &&
+ (config.tx_type != HWTSTAMP_TX_ON))
+ return -ERANGE;
+
+ switch (config.rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_ALL:
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+ config.rx_filter = HWTSTAMP_FILTER_ALL;
+ regval = xemacps_read(lp->baseaddr, XEMACPS_NWCTRL_OFFSET);
+ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET,
+ (regval | XEMACPS_NWCTRL_RXTSTAMP_MASK));
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ config.tx_type = HWTSTAMP_TX_ON;
+ lp->hwtstamp_config = config;
+
+ return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
+ -EFAULT : 0;
+}
+#endif /* CONFIG_XILINX_PS_EMAC_HWTSTAMP */
+
+/**
+ * xemacps_ioctl - ioctl entry point
+ * @ndev: network device
+ * @rq: interface request ioctl
+ * @cmd: command code
+ *
+ * Called when user issues an ioctl request to the network device.
+ **/
+static int xemacps_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
+{
+ struct net_local *lp = netdev_priv(ndev);
+ struct phy_device *phydev = lp->phy_dev;
+
+ if (!netif_running(ndev))
+ return -EINVAL;
+
+ if (!phydev)
+ return -ENODEV;
+
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ case SIOCGMIIREG:
+ case SIOCSMIIREG:
+ return phy_mii_ioctl(phydev, rq, cmd);
+#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP
+ case SIOCSHWTSTAMP:
+ return xemacps_hwtstamp_ioctl(ndev, rq, cmd);
+#endif
+ default:
+ dev_info(&lp->pdev->dev, "ioctl %d not implemented.\n", cmd);
+ return -EOPNOTSUPP;
+ }
+
+}
+
+/**
+ * xemacps_probe - Platform driver probe
+ * @pdev: Pointer to platform device structure
+ *
+ * Return 0 on success, negative value if error
+ */
+static int xemacps_probe(struct platform_device *pdev)
+{
+ struct resource *r_mem = NULL;
+ struct resource *r_irq = NULL;
+ struct net_device *ndev;
+ struct net_local *lp;
+ u32 regval = 0;
+ int rc = -ENXIO;
+
+ r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!r_mem || !r_irq) {
+ dev_err(&pdev->dev, "no IO resource defined.\n");
+ return -ENXIO;
+ }
+
+ ndev = alloc_etherdev(sizeof(*lp));
+ if (!ndev) {
+ dev_err(&pdev->dev, "etherdev allocation failed.\n");
+ return -ENOMEM;
+ }
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ lp = netdev_priv(ndev);
+ lp->pdev = pdev;
+ lp->ndev = ndev;
+
+ spin_lock_init(&lp->tx_lock);
+ spin_lock_init(&lp->rx_lock);
+ spin_lock_init(&lp->nwctrlreg_lock);
+
+ lp->baseaddr = devm_ioremap_resource(&pdev->dev, r_mem);
+ if (IS_ERR(lp->baseaddr)) {
+ dev_err(&pdev->dev, "failed to map baseaddress.\n");
+ rc = PTR_ERR(lp->baseaddr);
+ goto err_out_free_netdev;
+ }
+
+ dev_dbg(&lp->pdev->dev, "BASEADDRESS hw: %p virt: %p\n",
+ (void *)r_mem->start, lp->baseaddr);
+
+ ndev->irq = platform_get_irq(pdev, 0);
+
+ ndev->netdev_ops = &netdev_ops;
+ ndev->watchdog_timeo = TX_TIMEOUT;
+ ndev->ethtool_ops = &xemacps_ethtool_ops;
+ ndev->base_addr = r_mem->start;
+ ndev->features = NETIF_F_IP_CSUM | NETIF_F_SG;
+ netif_napi_add(ndev, &lp->napi, xemacps_rx_poll, XEMACPS_NAPI_WEIGHT);
+
+ lp->ip_summed = CHECKSUM_UNNECESSARY;
+
+ rc = register_netdev(ndev);
+ if (rc) {
+ dev_err(&pdev->dev, "Cannot register net device, aborting.\n");
+ goto err_out_free_netdev;
+ }
+
+ if (ndev->irq == 54)
+ lp->enetnum = 0;
+ else
+ lp->enetnum = 1;
+
+ lp->aperclk = devm_clk_get(&pdev->dev, "aper_clk");
+ if (IS_ERR(lp->aperclk)) {
+ dev_err(&pdev->dev, "aper_clk clock not found.\n");
+ rc = PTR_ERR(lp->aperclk);
+ goto err_out_unregister_netdev;
+ }
+ lp->devclk = devm_clk_get(&pdev->dev, "ref_clk");
+ if (IS_ERR(lp->devclk)) {
+ dev_err(&pdev->dev, "ref_clk clock not found.\n");
+ rc = PTR_ERR(lp->devclk);
+ goto err_out_unregister_netdev;
+ }
+
+ rc = clk_prepare_enable(lp->aperclk);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to enable APER clock.\n");
+ goto err_out_unregister_netdev;
+ }
+ rc = clk_prepare_enable(lp->devclk);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to enable device clock.\n");
+ goto err_out_clk_dis_aper;
+ }
+
+ lp->clk_rate_change_nb.notifier_call = xemacps_clk_notifier_cb;
+ lp->clk_rate_change_nb.next = NULL;
+ if (clk_notifier_register(lp->devclk, &lp->clk_rate_change_nb))
+ dev_warn(&pdev->dev,
+ "Unable to register clock notifier.\n");
+
+#ifdef CONFIG_XILINX_PS_EMAC_HWTSTAMP
+ prop = of_get_property(lp->pdev->dev.of_node,
+ "xlnx,ptp-enet-clock", NULL);
+ if (prop)
+ lp->ptpenetclk = (u32)be32_to_cpup(prop);
+ else
+ lp->ptpenetclk = 133333328;
+#endif
+
+ lp->phy_node = of_parse_phandle(lp->pdev->dev.of_node,
+ "phy-handle", 0);
+ lp->gmii2rgmii_phy_node = of_parse_phandle(lp->pdev->dev.of_node,
+ "gmii2rgmii-phy-handle", 0);
+ rc = of_get_phy_mode(lp->pdev->dev.of_node);
+ if (rc < 0) {
+ dev_err(&lp->pdev->dev, "error in getting phy i/f\n");
+ goto err_out_unregister_clk_notifier;
+ }
+
+ lp->phy_interface = rc;
+
+ /* Set MDIO clock divider */
+ regval = (MDC_DIV_224 << XEMACPS_NWCFG_MDC_SHIFT_MASK);
+ xemacps_write(lp->baseaddr, XEMACPS_NWCFG_OFFSET, regval);
+
+
+ regval = XEMACPS_NWCTRL_MDEN_MASK;
+ xemacps_write(lp->baseaddr, XEMACPS_NWCTRL_OFFSET, regval);
+
+ rc = xemacps_mii_init(lp);
+ if (rc) {
+ dev_err(&lp->pdev->dev, "error in xemacps_mii_init\n");
+ goto err_out_unregister_clk_notifier;
+ }
+
+ xemacps_update_hwaddr(lp);
+ tasklet_init(&lp->tx_bdreclaim_tasklet, xemacps_tx_poll,
+ (unsigned long) ndev);
+ tasklet_disable(&lp->tx_bdreclaim_tasklet);
+
+ lp->txtimeout_handler_wq = create_singlethread_workqueue(DRIVER_NAME);
+ INIT_WORK(&lp->txtimeout_reinit, xemacps_reinit_for_txtimeout);
+
+ platform_set_drvdata(pdev, ndev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ dev_info(&lp->pdev->dev, "pdev->id %d, baseaddr 0x%08lx, irq %d\n",
+ pdev->id, ndev->base_addr, ndev->irq);
+
+ rc = devm_request_irq(&pdev->dev, ndev->irq, &xemacps_interrupt, 0,
+ ndev->name, ndev);
+ if (rc) {
+ dev_err(&lp->pdev->dev, "Unable to request IRQ %p, error %d\n",
+ r_irq, rc);
+ goto err_out_unregister_clk_notifier;
+ }
+
+ return 0;
+
+err_out_unregister_clk_notifier:
+ clk_notifier_unregister(lp->devclk, &lp->clk_rate_change_nb);
+ clk_disable_unprepare(lp->devclk);
+err_out_clk_dis_aper:
+ clk_disable_unprepare(lp->aperclk);
+err_out_unregister_netdev:
+ unregister_netdev(ndev);
+err_out_free_netdev:
+ free_netdev(ndev);
+ platform_set_drvdata(pdev, NULL);
+ return rc;
+}
+
+/**
+ * xemacps_remove - called when platform driver is unregistered
+ * @pdev: Pointer to the platform device structure
+ *
+ * return: 0 on success
+ */
+static int xemacps_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_local *lp;
+
+ if (ndev) {
+ lp = netdev_priv(ndev);
+
+ mdiobus_unregister(lp->mii_bus);
+ kfree(lp->mii_bus->irq);
+ mdiobus_free(lp->mii_bus);
+ unregister_netdev(ndev);
+
+ clk_notifier_unregister(lp->devclk, &lp->clk_rate_change_nb);
+ if (!pm_runtime_suspended(&pdev->dev)) {
+ clk_disable_unprepare(lp->devclk);
+ clk_disable_unprepare(lp->aperclk);
+ } else {
+ clk_unprepare(lp->devclk);
+ clk_unprepare(lp->aperclk);
+ }
+
+ free_netdev(ndev);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
+/**
+ * xemacps_suspend - Suspend event
+ * @device: Pointer to device structure
+ *
+ * Return 0
+ */
+static int xemacps_suspend(struct device *device)
+{
+ struct platform_device *pdev = container_of(device,
+ struct platform_device, dev);
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_local *lp = netdev_priv(ndev);
+
+ netif_device_detach(ndev);
+ if (!pm_runtime_suspended(device)) {
+ clk_disable(lp->devclk);
+ clk_disable(lp->aperclk);
+ }
+ return 0;
+}
+
+/**
+ * xemacps_resume - Resume after previous suspend
+ * @pdev: Pointer to platform device structure
+ *
+ * Returns 0 on success, errno otherwise.
+ */
+static int xemacps_resume(struct device *device)
+{
+ struct platform_device *pdev = container_of(device,
+ struct platform_device, dev);
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_local *lp = netdev_priv(ndev);
+
+ if (!pm_runtime_suspended(device)) {
+ int ret;
+
+ ret = clk_enable(lp->aperclk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(lp->devclk);
+ if (ret) {
+ clk_disable(lp->aperclk);
+ return ret;
+ }
+ }
+ netif_device_attach(ndev);
+ return 0;
+}
+#endif /* ! CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM_RUNTIME
+static int xemacps_runtime_idle(struct device *dev)
+{
+ return pm_schedule_suspend(dev, 1);
+}
+
+static int xemacps_runtime_resume(struct device *device)
+{
+ int ret;
+ struct platform_device *pdev = container_of(device,
+ struct platform_device, dev);
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_local *lp = netdev_priv(ndev);
+
+ ret = clk_enable(lp->aperclk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(lp->devclk);
+ if (ret) {
+ clk_disable(lp->aperclk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int xemacps_runtime_suspend(struct device *device)
+{
+ struct platform_device *pdev = container_of(device,
+ struct platform_device, dev);
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_local *lp = netdev_priv(ndev);
+
+ clk_disable(lp->devclk);
+ clk_disable(lp->aperclk);
+ return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+static const struct dev_pm_ops xemacps_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(xemacps_suspend, xemacps_resume)
+ SET_RUNTIME_PM_OPS(xemacps_runtime_suspend, xemacps_runtime_resume,
+ xemacps_runtime_idle)
+};
+#define XEMACPS_PM (&xemacps_dev_pm_ops)
+#else /* ! CONFIG_PM */
+#define XEMACPS_PM NULL
+#endif /* ! CONFIG_PM */
+
+static struct net_device_ops netdev_ops = {
+ .ndo_open = xemacps_open,
+ .ndo_stop = xemacps_close,
+ .ndo_start_xmit = xemacps_start_xmit,
+ .ndo_set_rx_mode = xemacps_set_rx_mode,
+ .ndo_set_mac_address = xemacps_set_mac_address,
+ .ndo_do_ioctl = xemacps_ioctl,
+ .ndo_change_mtu = xemacps_change_mtu,
+ .ndo_tx_timeout = xemacps_tx_timeout,
+ .ndo_get_stats = xemacps_get_stats,
+};
+
+static struct of_device_id xemacps_of_match[] = {
+ { .compatible = "xlnx,ps7-ethernet-1.00.a", },
+ { /* end of table */}
+};
+MODULE_DEVICE_TABLE(of, xemacps_of_match);
+
+static struct platform_driver xemacps_driver = {
+ .probe = xemacps_probe,
+ .remove = xemacps_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = xemacps_of_match,
+ .pm = XEMACPS_PM,
+ },
+};
+
+module_platform_driver(xemacps_driver);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION("Xilinx Ethernet driver");
+MODULE_LICENSE("GPL v2");