aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/broadcom/genet/bcmgenet.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/broadcom/genet/bcmgenet.c')
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c170
1 files changed, 157 insertions, 13 deletions
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index da1a2500c91c..ff83c46bc389 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -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++;
@@ -2400,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;
@@ -2448,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))
@@ -2483,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;
@@ -2498,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);
@@ -2512,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);
@@ -2556,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))
@@ -2577,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;
@@ -2727,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;
@@ -2744,7 +2889,6 @@ static struct platform_driver bcmgenet_driver = {
.remove = bcmgenet_remove,
.driver = {
.name = "bcmgenet",
- .owner = THIS_MODULE,
.of_match_table = bcmgenet_match,
.pm = &bcmgenet_pm_ops,
},