Merge tag 'driver-core-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / net / ethernet / broadcom / genet / bcmgenet.c
index aafbefe..ff83c46 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/ip.h>
 #include <linux/ipv6.h>
 #include <linux/phy.h>
+#include <linux/platform_data/bcmgenet.h>
 
 #include <asm/unaligned.h>
 
@@ -613,6 +614,9 @@ static const struct bcmgenet_stats bcmgenet_gstrings_stats[] = {
                        UMAC_RBUF_OVFL_CNT),
        STAT_GENET_MISC("rbuf_err_cnt", mib.rbuf_err_cnt, UMAC_RBUF_ERR_CNT),
        STAT_GENET_MISC("mdf_err_cnt", mib.mdf_err_cnt, UMAC_MDF_ERR_CNT),
+       STAT_GENET_MIB_RX("alloc_rx_buff_failed", mib.alloc_rx_buff_failed),
+       STAT_GENET_MIB_RX("rx_dma_failed", mib.rx_dma_failed),
+       STAT_GENET_MIB_TX("tx_dma_failed", mib.tx_dma_failed),
 };
 
 #define BCMGENET_STATS_LEN     ARRAY_SIZE(bcmgenet_gstrings_stats)
@@ -711,6 +715,98 @@ static void bcmgenet_get_ethtool_stats(struct net_device *dev,
        }
 }
 
+static void bcmgenet_eee_enable_set(struct net_device *dev, bool enable)
+{
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+       u32 off = priv->hw_params->tbuf_offset + TBUF_ENERGY_CTRL;
+       u32 reg;
+
+       if (enable && !priv->clk_eee_enabled) {
+               clk_prepare_enable(priv->clk_eee);
+               priv->clk_eee_enabled = true;
+       }
+
+       reg = bcmgenet_umac_readl(priv, UMAC_EEE_CTRL);
+       if (enable)
+               reg |= EEE_EN;
+       else
+               reg &= ~EEE_EN;
+       bcmgenet_umac_writel(priv, reg, UMAC_EEE_CTRL);
+
+       /* Enable EEE and switch to a 27Mhz clock automatically */
+       reg = __raw_readl(priv->base + off);
+       if (enable)
+               reg |= TBUF_EEE_EN | TBUF_PM_EN;
+       else
+               reg &= ~(TBUF_EEE_EN | TBUF_PM_EN);
+       __raw_writel(reg, priv->base + off);
+
+       /* Do the same for thing for RBUF */
+       reg = bcmgenet_rbuf_readl(priv, RBUF_ENERGY_CTRL);
+       if (enable)
+               reg |= RBUF_EEE_EN | RBUF_PM_EN;
+       else
+               reg &= ~(RBUF_EEE_EN | RBUF_PM_EN);
+       bcmgenet_rbuf_writel(priv, reg, RBUF_ENERGY_CTRL);
+
+       if (!enable && priv->clk_eee_enabled) {
+               clk_disable_unprepare(priv->clk_eee);
+               priv->clk_eee_enabled = false;
+       }
+
+       priv->eee.eee_enabled = enable;
+       priv->eee.eee_active = enable;
+}
+
+static int bcmgenet_get_eee(struct net_device *dev, struct ethtool_eee *e)
+{
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+       struct ethtool_eee *p = &priv->eee;
+
+       if (GENET_IS_V1(priv))
+               return -EOPNOTSUPP;
+
+       e->eee_enabled = p->eee_enabled;
+       e->eee_active = p->eee_active;
+       e->tx_lpi_timer = bcmgenet_umac_readl(priv, UMAC_EEE_LPI_TIMER);
+
+       return phy_ethtool_get_eee(priv->phydev, e);
+}
+
+static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e)
+{
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+       struct ethtool_eee *p = &priv->eee;
+       int ret = 0;
+
+       if (GENET_IS_V1(priv))
+               return -EOPNOTSUPP;
+
+       p->eee_enabled = e->eee_enabled;
+
+       if (!p->eee_enabled) {
+               bcmgenet_eee_enable_set(dev, false);
+       } else {
+               ret = phy_init_eee(priv->phydev, 0);
+               if (ret) {
+                       netif_err(priv, hw, dev, "EEE initialization failed\n");
+                       return ret;
+               }
+
+               bcmgenet_umac_writel(priv, e->tx_lpi_timer, UMAC_EEE_LPI_TIMER);
+               bcmgenet_eee_enable_set(dev, true);
+       }
+
+       return phy_ethtool_set_eee(priv->phydev, e);
+}
+
+static int bcmgenet_nway_reset(struct net_device *dev)
+{
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+
+       return genphy_restart_aneg(priv->phydev);
+}
+
 /* standard ethtool support functions. */
 static struct ethtool_ops bcmgenet_ethtool_ops = {
        .get_strings            = bcmgenet_get_strings,
@@ -724,6 +820,9 @@ static struct ethtool_ops bcmgenet_ethtool_ops = {
        .set_msglevel           = bcmgenet_set_msglevel,
        .get_wol                = bcmgenet_get_wol,
        .set_wol                = bcmgenet_set_wol,
+       .get_eee                = bcmgenet_get_eee,
+       .set_eee                = bcmgenet_set_eee,
+       .nway_reset             = bcmgenet_nway_reset,
 };
 
 /* Power down the unimac, based on mode. */
@@ -989,6 +1088,7 @@ static int bcmgenet_xmit_single(struct net_device *dev,
        mapping = dma_map_single(kdev, skb->data, skb_len, DMA_TO_DEVICE);
        ret = dma_mapping_error(kdev, mapping);
        if (ret) {
+               priv->mib.tx_dma_failed++;
                netif_err(priv, tx_err, dev, "Tx DMA map failed\n");
                dev_kfree_skb(skb);
                return ret;
@@ -1035,6 +1135,7 @@ static int bcmgenet_xmit_frag(struct net_device *dev,
                                   skb_frag_size(frag), DMA_TO_DEVICE);
        ret = dma_mapping_error(kdev, mapping);
        if (ret) {
+               priv->mib.tx_dma_failed++;
                netif_err(priv, tx_err, dev, "%s: Tx DMA map failed\n",
                          __func__);
                return ret;
@@ -1231,6 +1332,7 @@ static int bcmgenet_rx_refill(struct bcmgenet_priv *priv, struct enet_cb *cb)
                                 priv->rx_buf_len, DMA_FROM_DEVICE);
        ret = dma_mapping_error(kdev, mapping);
        if (ret) {
+               priv->mib.rx_dma_failed++;
                bcmgenet_free_cb(cb);
                netif_err(priv, rx_err, priv->dev,
                          "%s DMA map failed\n", __func__);
@@ -1397,8 +1499,10 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_priv *priv,
                /* refill RX path on the current control block */
 refill:
                err = bcmgenet_rx_refill(priv, cb);
-               if (err)
+               if (err) {
+                       priv->mib.alloc_rx_buff_failed++;
                        netif_err(priv, rx_err, dev, "Rx refill failed\n");
+               }
 
                rxpktprocessed++;
                priv->rx_read_ptr++;
@@ -2140,6 +2244,12 @@ static int bcmgenet_open(struct net_device *dev)
                goto err_irq0;
        }
 
+       /* Re-configure the port multiplexer towards the PHY device */
+       bcmgenet_mii_config(priv->dev, false);
+
+       phy_connect_direct(dev, priv->phydev, bcmgenet_mii_setup,
+                          priv->phy_interface);
+
        bcmgenet_netif_start(dev);
 
        return 0;
@@ -2184,6 +2294,9 @@ static int bcmgenet_close(struct net_device *dev)
 
        bcmgenet_netif_stop(dev);
 
+       /* Really kill the PHY state machine and disconnect from it */
+       phy_disconnect(priv->phydev);
+
        /* Disable MAC receive */
        umac_enable_set(priv, CMD_RX_EN, false);
 
@@ -2391,6 +2504,7 @@ static void bcmgenet_set_hw_params(struct bcmgenet_priv *priv)
        struct bcmgenet_hw_params *params;
        u32 reg;
        u8 major;
+       u16 gphy_rev;
 
        if (GENET_IS_V4(priv)) {
                bcmgenet_dma_regs = bcmgenet_dma_regs_v3plus;
@@ -2439,8 +2553,29 @@ static void bcmgenet_set_hw_params(struct bcmgenet_priv *priv)
         * to pass this information to the PHY driver. The PHY driver expects
         * to find the PHY major revision in bits 15:8 while the GENET register
         * stores that information in bits 7:0, account for that.
+        *
+        * On newer chips, starting with PHY revision G0, a new scheme is
+        * deployed similar to the Starfighter 2 switch with GPHY major
+        * revision in bits 15:8 and patch level in bits 7:0. Major revision 0
+        * is reserved as well as special value 0x01ff, we have a small
+        * heuristic to check for the new GPHY revision and re-arrange things
+        * so the GPHY driver is happy.
         */
-       priv->gphy_rev = (reg & 0xffff) << 8;
+       gphy_rev = reg & 0xffff;
+
+       /* This is the good old scheme, just GPHY major, no minor nor patch */
+       if ((gphy_rev & 0xf0) != 0)
+               priv->gphy_rev = gphy_rev << 8;
+
+       /* This is the new scheme, GPHY major rolls over with 0x10 = rev G0 */
+       else if ((gphy_rev & 0xff00) != 0)
+               priv->gphy_rev = gphy_rev;
+
+       /* This is reserved so should require special treatment */
+       else if (gphy_rev == 0 || gphy_rev == 0x01ff) {
+               pr_warn("Invalid GPHY revision detected: 0x%04x\n", gphy_rev);
+               return;
+       }
 
 #ifdef CONFIG_PHYS_ADDR_T_64BIT
        if (!(params->flags & GENET_HAS_40BITS))
@@ -2474,8 +2609,9 @@ static const struct of_device_id bcmgenet_match[] = {
 
 static int bcmgenet_probe(struct platform_device *pdev)
 {
+       struct bcmgenet_platform_data *pd = pdev->dev.platform_data;
        struct device_node *dn = pdev->dev.of_node;
-       const struct of_device_id *of_id;
+       const struct of_device_id *of_id = NULL;
        struct bcmgenet_priv *priv;
        struct net_device *dev;
        const void *macaddr;
@@ -2489,9 +2625,11 @@ static int bcmgenet_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-       of_id = of_match_node(bcmgenet_match, dn);
-       if (!of_id)
-               return -EINVAL;
+       if (dn) {
+               of_id = of_match_node(bcmgenet_match, dn);
+               if (!of_id)
+                       return -EINVAL;
+       }
 
        priv = netdev_priv(dev);
        priv->irq0 = platform_get_irq(pdev, 0);
@@ -2503,11 +2641,15 @@ static int bcmgenet_probe(struct platform_device *pdev)
                goto err;
        }
 
-       macaddr = of_get_mac_address(dn);
-       if (!macaddr) {
-               dev_err(&pdev->dev, "can't find MAC address\n");
-               err = -EINVAL;
-               goto err;
+       if (dn) {
+               macaddr = of_get_mac_address(dn);
+               if (!macaddr) {
+                       dev_err(&pdev->dev, "can't find MAC address\n");
+                       err = -EINVAL;
+                       goto err;
+               }
+       } else {
+               macaddr = pd->mac_address;
        }
 
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -2547,7 +2689,10 @@ static int bcmgenet_probe(struct platform_device *pdev)
 
        priv->dev = dev;
        priv->pdev = pdev;
-       priv->version = (enum bcmgenet_version)of_id->data;
+       if (of_id)
+               priv->version = (enum bcmgenet_version)of_id->data;
+       else
+               priv->version = pd->genet_version;
 
        priv->clk = devm_clk_get(&priv->pdev->dev, "enet");
        if (IS_ERR(priv->clk))
@@ -2568,6 +2713,12 @@ static int bcmgenet_probe(struct platform_device *pdev)
        if (IS_ERR(priv->clk_wol))
                dev_warn(&priv->pdev->dev, "failed to get enet-wol clock\n");
 
+       priv->clk_eee = devm_clk_get(&priv->pdev->dev, "enet-eee");
+       if (IS_ERR(priv->clk_eee)) {
+               dev_warn(&priv->pdev->dev, "failed to get enet-eee clock\n");
+               priv->clk_eee = NULL;
+       }
+
        err = reset_umac(priv);
        if (err)
                goto err_clk_disable;
@@ -2685,7 +2836,7 @@ static int bcmgenet_resume(struct device *d)
 
        phy_init_hw(priv->phydev);
        /* Speed settings must be restored */
-       bcmgenet_mii_config(priv->dev);
+       bcmgenet_mii_config(priv->dev, false);
 
        /* disable ethernet MAC while updating its registers */
        umac_enable_set(priv, CMD_TX_EN | CMD_RX_EN, false);
@@ -2718,6 +2869,9 @@ static int bcmgenet_resume(struct device *d)
 
        phy_resume(priv->phydev);
 
+       if (priv->eee.eee_enabled)
+               bcmgenet_eee_enable_set(dev, true);
+
        bcmgenet_netif_start(dev);
 
        return 0;