diff options
Diffstat (limited to 'drivers/net/ethernet/ti/cpsw.c')
-rw-r--r-- | drivers/net/ethernet/ti/cpsw.c | 97 |
1 files changed, 77 insertions, 20 deletions
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index b988d16cd34e..e2a00287f8eb 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -699,6 +699,28 @@ static void cpsw_rx_handler(void *token, int len, int status) cpsw_dual_emac_src_port_detect(status, priv, ndev, skb); if (unlikely(status < 0) || unlikely(!netif_running(ndev))) { + bool ndev_status = false; + struct cpsw_slave *slave = priv->slaves; + int n; + + if (priv->data.dual_emac) { + /* In dual emac mode check for all interfaces */ + for (n = priv->data.slaves; n; n--, slave++) + if (netif_running(slave->ndev)) + ndev_status = true; + } + + if (ndev_status && (status >= 0)) { + /* The packet received is for the interface which + * is already down and the other interface is up + * and running, intead of freeing which results + * in reducing of the number of rx descriptor in + * DMA engine, requeue skb back to cpdma. + */ + new_skb = skb; + goto requeue; + } + /* the interface is going down, skbs are purged */ dev_kfree_skb_any(skb); return; @@ -717,6 +739,7 @@ static void cpsw_rx_handler(void *token, int len, int status) new_skb = skb; } +requeue: ret = cpdma_chan_submit(priv->rxch, new_skb, new_skb->data, skb_tailroom(new_skb), 0); if (WARN_ON(ret < 0)) @@ -884,14 +907,16 @@ static int cpsw_set_coalesce(struct net_device *ndev, u32 addnl_dvdr = 1; u32 coal_intvl = 0; - if (!coal->rx_coalesce_usecs) - return -EINVAL; - coal_intvl = coal->rx_coalesce_usecs; int_ctrl = readl(&priv->wr_regs->int_control); prescale = priv->bus_freq_mhz * 4; + if (!coal->rx_coalesce_usecs) { + int_ctrl &= ~(CPSW_INTPRESCALE_MASK | CPSW_INTPACEEN); + goto update_return; + } + if (coal_intvl < CPSW_CMINTMIN_INTVL) coal_intvl = CPSW_CMINTMIN_INTVL; @@ -919,6 +944,8 @@ static int cpsw_set_coalesce(struct net_device *ndev, int_ctrl |= CPSW_INTPACEEN; int_ctrl &= (~CPSW_INTPRESCALE_MASK); int_ctrl |= (prescale & CPSW_INTPRESCALE_MASK); + +update_return: writel(int_ctrl, &priv->wr_regs->int_control); cpsw_notice(priv, timer, "Set coalesce to %d usecs.\n", coal_intvl); @@ -999,17 +1026,6 @@ static void cpsw_get_ethtool_stats(struct net_device *ndev, } } -static inline int __show_stat(char *buf, int maxlen, const char *name, u32 val) -{ - static char *leader = "........................................"; - - if (!val) - return 0; - else - return snprintf(buf, maxlen, "%s %s %10d\n", name, - leader + strlen(name), val); -} - static int cpsw_common_res_usage_state(struct cpsw_priv *priv) { u32 i; @@ -1671,14 +1687,34 @@ static const struct net_device_ops cpsw_netdev_ops = { .ndo_vlan_rx_kill_vid = cpsw_ndo_vlan_rx_kill_vid, }; +static int cpsw_get_regs_len(struct net_device *ndev) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + + return priv->data.ale_entries * ALE_ENTRY_WORDS * sizeof(u32); +} + +static void cpsw_get_regs(struct net_device *ndev, + struct ethtool_regs *regs, void *p) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + u32 *reg = p; + + /* update CPSW IP version */ + regs->version = priv->version; + + cpsw_ale_dump(priv->ale, reg); +} + static void cpsw_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { struct cpsw_priv *priv = netdev_priv(ndev); - strlcpy(info->driver, "TI CPSW Driver v1.0", sizeof(info->driver)); + strlcpy(info->driver, "cpsw", sizeof(info->driver)); strlcpy(info->version, "1.0", sizeof(info->version)); strlcpy(info->bus_info, priv->pdev->name, sizeof(info->bus_info)); + info->regdump_len = cpsw_get_regs_len(ndev); } static u32 cpsw_get_msglevel(struct net_device *ndev) @@ -1786,6 +1822,8 @@ static const struct ethtool_ops cpsw_ethtool_ops = { .get_ethtool_stats = cpsw_get_ethtool_stats, .get_wol = cpsw_get_wol, .set_wol = cpsw_set_wol, + .get_regs_len = cpsw_get_regs_len, + .get_regs = cpsw_get_regs, }; static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv, @@ -2296,10 +2334,19 @@ static int cpsw_suspend(struct device *dev) struct net_device *ndev = platform_get_drvdata(pdev); struct cpsw_priv *priv = netdev_priv(ndev); - if (netif_running(ndev)) - cpsw_ndo_stop(ndev); + if (priv->data.dual_emac) { + int i; - for_each_slave(priv, soft_reset_slave); + for (i = 0; i < priv->data.slaves; i++) { + if (netif_running(priv->slaves[i].ndev)) + cpsw_ndo_stop(priv->slaves[i].ndev); + soft_reset_slave(priv->slaves + i); + } + } else { + if (netif_running(ndev)) + cpsw_ndo_stop(ndev); + for_each_slave(priv, soft_reset_slave); + } pm_runtime_put_sync(&pdev->dev); @@ -2313,14 +2360,24 @@ static int cpsw_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct net_device *ndev = platform_get_drvdata(pdev); + struct cpsw_priv *priv = netdev_priv(ndev); pm_runtime_get_sync(&pdev->dev); /* Select default pin state */ pinctrl_pm_select_default_state(&pdev->dev); - if (netif_running(ndev)) - cpsw_ndo_open(ndev); + if (priv->data.dual_emac) { + int i; + + for (i = 0; i < priv->data.slaves; i++) { + if (netif_running(priv->slaves[i].ndev)) + cpsw_ndo_open(priv->slaves[i].ndev); + } + } else { + if (netif_running(ndev)) + cpsw_ndo_open(ndev); + } return 0; } |