]> rtime.felk.cvut.cz Git - zynq/linux.git/commitdiff
net: ethernet: xilinx: Add support for 10G/25G MAC
authorKedareswara rao Appana <appanad@xilinx.com>
Mon, 18 Sep 2017 07:27:21 +0000 (12:57 +0530)
committerMichal Simek <michal.simek@xilinx.com>
Thu, 15 Mar 2018 14:17:47 +0000 (15:17 +0100)
This patch adds support for 10G/25G MAC.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
drivers/net/ethernet/xilinx/xilinx_axienet.h
drivers/net/ethernet/xilinx/xilinx_axienet_main.c

index e00d664cc941ec42bec3de8718c18b1c01fe3805..17d6b1ef6e224a21cbf288854ed8e25611c17df3 100644 (file)
 #endif
 #endif
 
+/* XXV MAC Register Definitions */
+#define XXV_TC_OFFSET                  0x0000000C
+#define XXV_RCW1_OFFSET                        0x00000014
+#define XXV_JUM_OFFSET                 0x00000018
+#define XXV_TICKREG_OFFSET             0x00000020
+#define XXV_STATRX_BLKLCK_OFFSET       0x0000040C
+
+/* XXV MAC Register Mask Definitions */
+#define XXV_TC_TX_MASK         BIT(0)
+#define XXV_RCW1_RX_MASK       BIT(0)
+#define XXV_RCW1_FCS_MASK      BIT(1)
+#define XXV_TC_FCS_MASK                BIT(1)
+#define XXV_MIN_JUM_MASK       GENMASK(7, 0)
+#define XXV_MAX_JUM_MASK       GENMASK(10, 8)
+#define XXV_RX_BLKLCK_MASK     BIT(0)
+#define XXV_TICKREG_STATEN_MASK BIT(0)
+#define XXV_MAC_MIN_PKT_LEN    64
+
 /**
  * struct axidma_bd - Axi Dma buffer descriptor layout
  * @next:         MM2S/S2MM Next Descriptor Pointer
@@ -487,12 +505,14 @@ struct axienet_local {
  * @XAXIENET_1G:        IP is 1G MAC
  * @XAXIENET_2_5G:      IP type is 2.5G MAC.
  * @XAXIENET_LEGACY_10G: IP type is legacy 10G MAC.
+ * @XAXIENET_10G_25G:   IP type is 10G/25G MAC(XXV MAC).
  *
  */
 enum axienet_ip_type {
        XAXIENET_1G = 0,
        XAXIENET_2_5G,
        XAXIENET_LEGACY_10G,
+       XAXIENET_10G_25G,
 };
 
 struct axienet_config {
@@ -512,6 +532,12 @@ struct axienet_option {
        u32 m_or;
 };
 
+struct xxvenet_option {
+       u32 opt;
+       u32 reg;
+       u32 m_or;
+};
+
 /**
  * axienet_ior - Memory mapped Axi Ethernet register read
  * @lp:         Pointer to axienet local structure
index f2f80aa352e6445c89146cc711251f52a2657709..4caedc0f3ad21ec5d8716dee4b2a9bd57d38122d 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/phy.h>
 #include <linux/mii.h>
 #include <linux/ethtool.h>
+#include <linux/iopoll.h>
 
 #include "xilinx_axienet.h"
 
@@ -104,6 +105,28 @@ static struct axienet_option axienet_options[] = {
        {}
 };
 
+/* Option table for setting up Axi Ethernet hardware options */
+static struct xxvenet_option xxvenet_options[] = {
+       { /* Turn on FCS stripping on receive packets */
+               .opt = XAE_OPTION_FCS_STRIP,
+               .reg = XXV_RCW1_OFFSET,
+               .m_or = XXV_RCW1_FCS_MASK,
+       }, { /* Turn on FCS insertion on transmit packets */
+               .opt = XAE_OPTION_FCS_INSERT,
+               .reg = XXV_TC_OFFSET,
+               .m_or = XXV_TC_FCS_MASK,
+       }, { /* Enable transmitter */
+               .opt = XAE_OPTION_TXEN,
+               .reg = XXV_TC_OFFSET,
+               .m_or = XXV_TC_TX_MASK,
+       }, { /* Enable receiver */
+               .opt = XAE_OPTION_RXEN,
+               .reg = XXV_RCW1_OFFSET,
+               .m_or = XXV_RCW1_RX_MASK,
+       },
+       {}
+};
+
 /**
  * axienet_dma_in32 - Memory mapped Axi DMA register read
  * @lp:                Pointer to axienet local structure
@@ -436,6 +459,23 @@ static void axienet_setoptions(struct net_device *ndev, u32 options)
        lp->options |= options;
 }
 
+static void xxvenet_setoptions(struct net_device *ndev, u32 options)
+{
+       int reg;
+       struct axienet_local *lp = netdev_priv(ndev);
+       struct xxvenet_option *tp = &xxvenet_options[0];
+
+       while (tp->opt) {
+               reg = ((axienet_ior(lp, tp->reg)) & ~(tp->m_or));
+               if (options & tp->opt)
+                       reg |= tp->m_or;
+               axienet_iow(lp, tp->reg, reg);
+               tp++;
+       }
+
+       lp->options |= options;
+}
+
 static void __axienet_device_reset(struct axienet_local *lp, off_t offset)
 {
        u32 timeout;
@@ -471,20 +511,23 @@ static void axienet_device_reset(struct net_device *ndev)
 {
        u32 axienet_status;
        struct axienet_local *lp = netdev_priv(ndev);
+       u32 err, val;
 
        __axienet_device_reset(lp, XAXIDMA_TX_CR_OFFSET);
        __axienet_device_reset(lp, XAXIDMA_RX_CR_OFFSET);
 
        lp->max_frm_size = XAE_MAX_VLAN_FRAME_SIZE;
-       lp->options |= XAE_OPTION_VLAN;
-       lp->options &= (~XAE_OPTION_JUMBO);
+       if (lp->axienet_config->mactype != XAXIENET_10G_25G) {
+               lp->options |= XAE_OPTION_VLAN;
+               lp->options &= (~XAE_OPTION_JUMBO);
+       }
 
        if ((ndev->mtu > XAE_MTU) &&
                (ndev->mtu <= XAE_JUMBO_MTU)) {
                lp->max_frm_size = ndev->mtu + VLAN_ETH_HLEN +
                                        XAE_TRL_SIZE;
-
-               if (lp->max_frm_size <= lp->rxmem)
+               if (lp->max_frm_size <= lp->rxmem &&
+                   (lp->axienet_config->mactype != XAXIENET_10G_25G))
                        lp->options |= XAE_OPTION_JUMBO;
        }
 
@@ -493,26 +536,47 @@ static void axienet_device_reset(struct net_device *ndev)
                           __func__);
        }
 
-       axienet_status = axienet_ior(lp, XAE_RCW1_OFFSET);
-       axienet_status &= ~XAE_RCW1_RX_MASK;
-       axienet_iow(lp, XAE_RCW1_OFFSET, axienet_status);
+       if (lp->axienet_config->mactype != XAXIENET_10G_25G) {
+               axienet_status = axienet_ior(lp, XAE_RCW1_OFFSET);
+               axienet_status &= ~XAE_RCW1_RX_MASK;
+               axienet_iow(lp, XAE_RCW1_OFFSET, axienet_status);
+       }
+
+       if (lp->axienet_config->mactype == XAXIENET_10G_25G) {
+               /* Check for block lock bit got set or not
+                * This ensures that 10G ethernet IP
+                * is functioning normally or not.
+                */
+               err = readl_poll_timeout(lp->regs + XXV_STATRX_BLKLCK_OFFSET,
+                                        val, (val & XXV_RX_BLKLCK_MASK),
+                                        10, DELAY_OF_ONE_MILLISEC);
+               if (err) {
+                       netdev_err(ndev, "%s: Block lock bit of XXV MAC didn't",
+                                  __func__);
+                       netdev_err(ndev, "Got Set cross check the ref clock");
+                       netdev_err(ndev, "Configuration for the mac");
+               }
+       }
 
-       if (!lp->eth_hasnobuf) {
+       if ((lp->axienet_config->mactype == XAXIENET_1G) &&
+           !lp->eth_hasnobuf) {
                axienet_status = axienet_ior(lp, XAE_IP_OFFSET);
                if (axienet_status & XAE_INT_RXRJECT_MASK)
                        axienet_iow(lp, XAE_IS_OFFSET, XAE_INT_RXRJECT_MASK);
        }
 
-       axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
+       if (lp->axienet_config->mactype == XAXIENET_10G_25G) {
+               lp->options |= XAE_OPTION_FCS_STRIP;
+               lp->options |= XAE_OPTION_FCS_INSERT;
+       } else {
+               axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
+       }
+       lp->axienet_config->setoptions(ndev, lp->options &
+                                      ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
 
-       /* Sync default options with HW but leave receiver and
-        * transmitter disabled.
-        */
-       axienet_setoptions(ndev, lp->options &
-                          ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
        axienet_set_mac_address(ndev, NULL);
        axienet_set_multicast_list(ndev);
-       axienet_setoptions(ndev, lp->options);
+       lp->axienet_config->setoptions(ndev, lp->options);
 
        netif_trans_update(ndev);
 }
@@ -671,6 +735,7 @@ static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        struct axienet_local *lp = netdev_priv(ndev);
        struct axidma_bd *cur_p;
        unsigned long flags;
+       u32 pad = 0;
 
        num_frag = skb_shinfo(skb)->nr_frags;
        cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
@@ -683,6 +748,14 @@ static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                return NETDEV_TX_BUSY;
        }
 
+       /* Work around for XXV MAC as MAC will drop the packets
+        * of size less than 64 bytes we need to append data
+        * to make packet length greater than or equal to 64
+        */
+       if (skb->len < XXV_MAC_MIN_PKT_LEN &&
+           (lp->axienet_config->mactype == XAXIENET_10G_25G))
+               pad = XXV_MAC_MIN_PKT_LEN - skb->len;
+
        if (skb->ip_summed == CHECKSUM_PARTIAL && !lp->eth_hasnobuf &&
            (lp->axienet_config->mactype == XAXIENET_1G)) {
                if (lp->features & XAE_FEATURE_FULL_TX_CSUM) {
@@ -701,7 +774,7 @@ static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                cur_p->app0 |= 2; /* Tx Full Checksum Offload Enabled */
        }
 
-       cur_p->cntrl = skb_headlen(skb) | XAXIDMA_BD_CTRL_TXSOF_MASK;
+       cur_p->cntrl = (skb_headlen(skb) | XAXIDMA_BD_CTRL_TXSOF_MASK) + pad;
        cur_p->phys = dma_map_single(ndev->dev.parent, skb->data,
                                     skb_headlen(skb), DMA_TO_DEVICE);
 
@@ -714,7 +787,7 @@ static int axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                                             skb_frag_address(frag),
                                             skb_frag_size(frag),
                                             DMA_TO_DEVICE);
-               cur_p->cntrl = skb_frag_size(frag);
+               cur_p->cntrl = skb_frag_size(frag) + pad;
        }
 
        cur_p->cntrl |= XAXIDMA_BD_CTRL_TXEOF_MASK;
@@ -983,6 +1056,33 @@ static irqreturn_t axienet_rx_irq(int irq, void *_ndev)
 
 static void axienet_dma_err_handler(unsigned long data);
 
+static int axienet_mii_init(struct net_device *ndev)
+{
+       struct axienet_local *lp = netdev_priv(ndev);
+       int ret, mdio_mcreg;
+
+       mdio_mcreg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
+       ret = axienet_mdio_wait_until_ready(lp);
+       if (ret < 0)
+               return ret;
+
+       /* Disable the MDIO interface till Axi Ethernet Reset is completed.
+        * When we do an Axi Ethernet reset, it resets the complete core
+        * Including the MDIO. If MDIO is not disabled when the reset process is
+        * Started, MDIO will be broken afterwards.
+        */
+       axienet_iow(lp, XAE_MDIO_MC_OFFSET,
+                   (mdio_mcreg & (~XAE_MDIO_MC_MDIOEN_MASK)));
+       axienet_device_reset(ndev);
+       /* Enable the MDIO */
+       axienet_iow(lp, XAE_MDIO_MC_OFFSET, mdio_mcreg);
+       ret = axienet_mdio_wait_until_ready(lp);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
 /**
  * axienet_open - Driver open routine.
  * @ndev:      Pointer to net_device structure
@@ -998,27 +1098,16 @@ static void axienet_dma_err_handler(unsigned long data);
  */
 static int axienet_open(struct net_device *ndev)
 {
-       int ret, mdio_mcreg;
+       int ret = 0;
        struct axienet_local *lp = netdev_priv(ndev);
        struct phy_device *phydev = NULL;
 
        dev_dbg(&ndev->dev, "axienet_open()\n");
 
-       mdio_mcreg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
-       ret = axienet_mdio_wait_until_ready(lp);
-       if (ret < 0)
-               return ret;
-       /* Disable the MDIO interface till Axi Ethernet Reset is completed.
-        * When we do an Axi Ethernet reset, it resets the complete core
-        * including the MDIO. If MDIO is not disabled when the reset
-        * process is started, MDIO will be broken afterwards.
-        */
-       axienet_iow(lp, XAE_MDIO_MC_OFFSET,
-                   (mdio_mcreg & (~XAE_MDIO_MC_MDIOEN_MASK)));
-       axienet_device_reset(ndev);
-       /* Enable the MDIO */
-       axienet_iow(lp, XAE_MDIO_MC_OFFSET, mdio_mcreg);
-       ret = axienet_mdio_wait_until_ready(lp);
+       if (lp->axienet_config->mactype == XAXIENET_10G_25G)
+               axienet_device_reset(ndev);
+       else
+               ret = axienet_mii_init(ndev);
        if (ret < 0)
                return ret;
 
@@ -1101,8 +1190,8 @@ static int axienet_stop(struct net_device *ndev)
        cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
        axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET,
                          cr & (~XAXIDMA_CR_RUNSTOP_MASK));
-       axienet_setoptions(ndev, lp->options &
-                          ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
+       lp->axienet_config->setoptions(ndev, lp->options &
+                                      ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
 
        napi_disable(&lp->napi);
        tasklet_kill(&lp->dma_err_tasklet);
@@ -1427,23 +1516,29 @@ static void axienet_dma_err_handler(unsigned long data)
        struct net_device *ndev = lp->ndev;
        struct axidma_bd *cur_p;
 
-       axienet_setoptions(ndev, lp->options &
-                          ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
-       mdio_mcreg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
-       axienet_mdio_wait_until_ready(lp);
-       /* Disable the MDIO interface till Axi Ethernet Reset is completed.
-        * When we do an Axi Ethernet reset, it resets the complete core
-        * including the MDIO. So if MDIO is not disabled when the reset
-        * process is started, MDIO will be broken afterwards.
-        */
-       axienet_iow(lp, XAE_MDIO_MC_OFFSET, (mdio_mcreg &
-                   ~XAE_MDIO_MC_MDIOEN_MASK));
+       lp->axienet_config->setoptions(ndev, lp->options &
+                                      ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
+
+       if (lp->axienet_config->mactype != XAXIENET_10G_25G) {
+               mdio_mcreg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
+               axienet_mdio_wait_until_ready(lp);
+               /* Disable the MDIO interface till Axi Ethernet Reset is
+                * Completed. When we do an Axi Ethernet reset, it resets the
+                * Complete core including the MDIO. So if MDIO is not disabled
+                * When the reset process is started,
+                * MDIO will be broken afterwards.
+                */
+               axienet_iow(lp, XAE_MDIO_MC_OFFSET, (mdio_mcreg &
+                           ~XAE_MDIO_MC_MDIOEN_MASK));
+       }
 
        __axienet_device_reset(lp, XAXIDMA_TX_CR_OFFSET);
        __axienet_device_reset(lp, XAXIDMA_RX_CR_OFFSET);
 
-       axienet_iow(lp, XAE_MDIO_MC_OFFSET, mdio_mcreg);
-       axienet_mdio_wait_until_ready(lp);
+       if (lp->axienet_config->mactype != XAXIENET_10G_25G) {
+               axienet_iow(lp, XAE_MDIO_MC_OFFSET, mdio_mcreg);
+               axienet_mdio_wait_until_ready(lp);
+       }
 
        for (i = 0; i < TX_BD_NUM; i++) {
                cur_p = &lp->tx_bd_v[i];
@@ -1524,26 +1619,26 @@ static void axienet_dma_err_handler(unsigned long data)
        axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET,
                          cr | XAXIDMA_CR_RUNSTOP_MASK);
 
-       axienet_status = axienet_ior(lp, XAE_RCW1_OFFSET);
-       axienet_status &= ~XAE_RCW1_RX_MASK;
-       axienet_iow(lp, XAE_RCW1_OFFSET, axienet_status);
+       if (lp->axienet_config->mactype != XAXIENET_10G_25G) {
+               axienet_status = axienet_ior(lp, XAE_RCW1_OFFSET);
+               axienet_status &= ~XAE_RCW1_RX_MASK;
+               axienet_iow(lp, XAE_RCW1_OFFSET, axienet_status);
+       }
 
-       if (!lp->eth_hasnobuf) {
+       if ((lp->axienet_config->mactype == XAXIENET_1G) && !lp->eth_hasnobuf) {
                axienet_status = axienet_ior(lp, XAE_IP_OFFSET);
                if (axienet_status & XAE_INT_RXRJECT_MASK)
                        axienet_iow(lp, XAE_IS_OFFSET, XAE_INT_RXRJECT_MASK);
        }
 
-       axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
+       if (lp->axienet_config->mactype != XAXIENET_10G_25G)
+               axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
 
-       /* Sync default options with HW but leave receiver and
-        * transmitter disabled.
-        */
-       axienet_setoptions(ndev, lp->options &
-                          ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
+       lp->axienet_config->setoptions(ndev, lp->options &
+                                      ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
        axienet_set_mac_address(ndev, NULL);
        axienet_set_multicast_list(ndev);
-       axienet_setoptions(ndev, lp->options);
+       lp->axienet_config->setoptions(ndev, lp->options);
 }
 
 static const struct axienet_config axienet_1g_config = {
@@ -1561,6 +1656,11 @@ static const struct axienet_config axienet_10g_config = {
        .setoptions = axienet_setoptions,
 };
 
+static const struct axienet_config axienet_10g25g_config = {
+       .mactype = XAXIENET_10G_25G,
+       .setoptions = xxvenet_setoptions,
+};
+
 /* Match table for of_platform binding */
 static const struct of_device_id axienet_of_match[] = {
        { .compatible = "xlnx,axi-ethernet-1.00.a", .data = &axienet_1g_config},
@@ -1569,6 +1669,8 @@ static const struct of_device_id axienet_of_match[] = {
        { .compatible = "xlnx,axi-2_5-gig-ethernet-1.0",
                                                .data = &axienet_2_5g_config},
        { .compatible = "xlnx,ten-gig-eth-mac", .data = &axienet_10g_config},
+       { .compatible = "xlnx,xxv-ethernet-1.0",
+                                               .data = &axienet_10g25g_config},
        {},
 };