|
@@ -133,6 +133,12 @@ static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
|
|
|
NETIF_MSG_LINK | NETIF_MSG_IFUP |
|
|
|
NETIF_MSG_IFDOWN | NETIF_MSG_TIMER);
|
|
|
|
|
|
+#define STMMAC_DEFAULT_LPI_TIMER 1000
|
|
|
+static int eee_timer = STMMAC_DEFAULT_LPI_TIMER;
|
|
|
+module_param(eee_timer, int, S_IRUGO | S_IWUSR);
|
|
|
+MODULE_PARM_DESC(eee_timer, "LPI tx expiration time in msec");
|
|
|
+#define STMMAC_LPI_TIMER(x) (jiffies + msecs_to_jiffies(x))
|
|
|
+
|
|
|
static irqreturn_t stmmac_interrupt(int irq, void *dev_id);
|
|
|
|
|
|
#ifdef CONFIG_STMMAC_DEBUG_FS
|
|
@@ -161,6 +167,8 @@ static void stmmac_verify_args(void)
|
|
|
flow_ctrl = FLOW_OFF;
|
|
|
if (unlikely((pause < 0) || (pause > 0xffff)))
|
|
|
pause = PAUSE_TIME;
|
|
|
+ if (eee_timer < 0)
|
|
|
+ eee_timer = STMMAC_DEFAULT_LPI_TIMER;
|
|
|
}
|
|
|
|
|
|
static void stmmac_clk_csr_set(struct stmmac_priv *priv)
|
|
@@ -229,6 +237,85 @@ static inline void stmmac_hw_fix_mac_speed(struct stmmac_priv *priv)
|
|
|
phydev->speed);
|
|
|
}
|
|
|
|
|
|
+static void stmmac_enable_eee_mode(struct stmmac_priv *priv)
|
|
|
+{
|
|
|
+ /* Check and enter in LPI mode */
|
|
|
+ if ((priv->dirty_tx == priv->cur_tx) &&
|
|
|
+ (priv->tx_path_in_lpi_mode == false))
|
|
|
+ priv->hw->mac->set_eee_mode(priv->ioaddr);
|
|
|
+}
|
|
|
+
|
|
|
+void stmmac_disable_eee_mode(struct stmmac_priv *priv)
|
|
|
+{
|
|
|
+ /* Exit and disable EEE in case of we are are in LPI state. */
|
|
|
+ priv->hw->mac->reset_eee_mode(priv->ioaddr);
|
|
|
+ del_timer_sync(&priv->eee_ctrl_timer);
|
|
|
+ priv->tx_path_in_lpi_mode = false;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * stmmac_eee_ctrl_timer
|
|
|
+ * @arg : data hook
|
|
|
+ * Description:
|
|
|
+ * If there is no data transfer and if we are not in LPI state,
|
|
|
+ * then MAC Transmitter can be moved to LPI state.
|
|
|
+ */
|
|
|
+static void stmmac_eee_ctrl_timer(unsigned long arg)
|
|
|
+{
|
|
|
+ struct stmmac_priv *priv = (struct stmmac_priv *)arg;
|
|
|
+
|
|
|
+ stmmac_enable_eee_mode(priv);
|
|
|
+ mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_TIMER(eee_timer));
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * stmmac_eee_init
|
|
|
+ * @priv: private device pointer
|
|
|
+ * Description:
|
|
|
+ * If the EEE support has been enabled while configuring the driver,
|
|
|
+ * if the GMAC actually supports the EEE (from the HW cap reg) and the
|
|
|
+ * phy can also manage EEE, so enable the LPI state and start the timer
|
|
|
+ * to verify if the tx path can enter in LPI state.
|
|
|
+ */
|
|
|
+bool stmmac_eee_init(struct stmmac_priv *priv)
|
|
|
+{
|
|
|
+ bool ret = false;
|
|
|
+
|
|
|
+ /* MAC core supports the EEE feature. */
|
|
|
+ if (priv->dma_cap.eee) {
|
|
|
+ /* Check if the PHY supports EEE */
|
|
|
+ if (phy_init_eee(priv->phydev, 1))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ priv->eee_active = 1;
|
|
|
+ init_timer(&priv->eee_ctrl_timer);
|
|
|
+ priv->eee_ctrl_timer.function = stmmac_eee_ctrl_timer;
|
|
|
+ priv->eee_ctrl_timer.data = (unsigned long)priv;
|
|
|
+ priv->eee_ctrl_timer.expires = STMMAC_LPI_TIMER(eee_timer);
|
|
|
+ add_timer(&priv->eee_ctrl_timer);
|
|
|
+
|
|
|
+ priv->hw->mac->set_eee_timer(priv->ioaddr,
|
|
|
+ STMMAC_DEFAULT_LIT_LS_TIMER,
|
|
|
+ priv->tx_lpi_timer);
|
|
|
+
|
|
|
+ pr_info("stmmac: Energy-Efficient Ethernet initialized\n");
|
|
|
+
|
|
|
+ ret = true;
|
|
|
+ }
|
|
|
+out:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void stmmac_eee_adjust(struct stmmac_priv *priv)
|
|
|
+{
|
|
|
+ /* When the EEE has been already initialised we have to
|
|
|
+ * modify the PLS bit in the LPI ctrl & status reg according
|
|
|
+ * to the PHY link status. For this reason.
|
|
|
+ */
|
|
|
+ if (priv->eee_enabled)
|
|
|
+ priv->hw->mac->set_eee_pls(priv->ioaddr, priv->phydev->link);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* stmmac_adjust_link
|
|
|
* @dev: net device structure
|
|
@@ -249,6 +336,7 @@ static void stmmac_adjust_link(struct net_device *dev)
|
|
|
phydev->addr, phydev->link);
|
|
|
|
|
|
spin_lock_irqsave(&priv->lock, flags);
|
|
|
+
|
|
|
if (phydev->link) {
|
|
|
u32 ctrl = readl(priv->ioaddr + MAC_CTRL_REG);
|
|
|
|
|
@@ -315,6 +403,8 @@ static void stmmac_adjust_link(struct net_device *dev)
|
|
|
if (new_state && netif_msg_link(priv))
|
|
|
phy_print_status(phydev);
|
|
|
|
|
|
+ stmmac_eee_adjust(priv);
|
|
|
+
|
|
|
spin_unlock_irqrestore(&priv->lock, flags);
|
|
|
|
|
|
DBG(probe, DEBUG, "stmmac_adjust_link: exiting\n");
|
|
@@ -332,7 +422,7 @@ static int stmmac_init_phy(struct net_device *dev)
|
|
|
{
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
struct phy_device *phydev;
|
|
|
- char phy_id[MII_BUS_ID_SIZE + 3];
|
|
|
+ char phy_id_fmt[MII_BUS_ID_SIZE + 3];
|
|
|
char bus_id[MII_BUS_ID_SIZE];
|
|
|
int interface = priv->plat->interface;
|
|
|
priv->oldlink = 0;
|
|
@@ -346,11 +436,12 @@ static int stmmac_init_phy(struct net_device *dev)
|
|
|
snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x",
|
|
|
priv->plat->bus_id);
|
|
|
|
|
|
- snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id,
|
|
|
+ snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id,
|
|
|
priv->plat->phy_addr);
|
|
|
- pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id);
|
|
|
+ pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id_fmt);
|
|
|
|
|
|
- phydev = phy_connect(dev, phy_id, &stmmac_adjust_link, 0, interface);
|
|
|
+ phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, 0,
|
|
|
+ interface);
|
|
|
|
|
|
if (IS_ERR(phydev)) {
|
|
|
pr_err("%s: Could not attach to PHY\n", dev->name);
|
|
@@ -689,6 +780,11 @@ static void stmmac_tx(struct stmmac_priv *priv)
|
|
|
}
|
|
|
netif_tx_unlock(priv->dev);
|
|
|
}
|
|
|
+
|
|
|
+ if ((priv->eee_enabled) && (!priv->tx_path_in_lpi_mode)) {
|
|
|
+ stmmac_enable_eee_mode(priv);
|
|
|
+ mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_TIMER(eee_timer));
|
|
|
+ }
|
|
|
spin_unlock(&priv->tx_lock);
|
|
|
}
|
|
|
|
|
@@ -1027,6 +1123,17 @@ static int stmmac_open(struct net_device *dev)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /* Request the IRQ lines */
|
|
|
+ if (priv->lpi_irq != -ENXIO) {
|
|
|
+ ret = request_irq(priv->lpi_irq, stmmac_interrupt, IRQF_SHARED,
|
|
|
+ dev->name, dev);
|
|
|
+ if (unlikely(ret < 0)) {
|
|
|
+ pr_err("%s: ERROR: allocating the LPI IRQ %d (%d)\n",
|
|
|
+ __func__, priv->lpi_irq, ret);
|
|
|
+ goto open_error_lpiirq;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/* Enable the MAC Rx/Tx */
|
|
|
stmmac_set_mac(priv->ioaddr, true);
|
|
|
|
|
@@ -1062,12 +1169,19 @@ static int stmmac_open(struct net_device *dev)
|
|
|
if (priv->phydev)
|
|
|
phy_start(priv->phydev);
|
|
|
|
|
|
+ priv->tx_lpi_timer = STMMAC_DEFAULT_TWT_LS_TIMER;
|
|
|
+ priv->eee_enabled = stmmac_eee_init(priv);
|
|
|
+
|
|
|
napi_enable(&priv->napi);
|
|
|
skb_queue_head_init(&priv->rx_recycle);
|
|
|
netif_start_queue(dev);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
+open_error_lpiirq:
|
|
|
+ if (priv->wol_irq != dev->irq)
|
|
|
+ free_irq(priv->wol_irq, dev);
|
|
|
+
|
|
|
open_error_wolirq:
|
|
|
free_irq(dev->irq, dev);
|
|
|
|
|
@@ -1093,6 +1207,9 @@ static int stmmac_release(struct net_device *dev)
|
|
|
{
|
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
|
|
+ if (priv->eee_enabled)
|
|
|
+ del_timer_sync(&priv->eee_ctrl_timer);
|
|
|
+
|
|
|
/* Stop and disconnect the PHY */
|
|
|
if (priv->phydev) {
|
|
|
phy_stop(priv->phydev);
|
|
@@ -1115,6 +1232,8 @@ static int stmmac_release(struct net_device *dev)
|
|
|
free_irq(dev->irq, dev);
|
|
|
if (priv->wol_irq != dev->irq)
|
|
|
free_irq(priv->wol_irq, dev);
|
|
|
+ if (priv->lpi_irq != -ENXIO)
|
|
|
+ free_irq(priv->lpi_irq, dev);
|
|
|
|
|
|
/* Stop TX/RX DMA and clear the descriptors */
|
|
|
priv->hw->dma->stop_tx(priv->ioaddr);
|
|
@@ -1164,6 +1283,9 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
|
|
|
spin_lock(&priv->tx_lock);
|
|
|
|
|
|
+ if (priv->tx_path_in_lpi_mode)
|
|
|
+ stmmac_disable_eee_mode(priv);
|
|
|
+
|
|
|
entry = priv->cur_tx % txsize;
|
|
|
|
|
|
#ifdef STMMAC_XMIT_DEBUG
|
|
@@ -1540,10 +1662,37 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
|
|
|
return IRQ_NONE;
|
|
|
}
|
|
|
|
|
|
- if (priv->plat->has_gmac)
|
|
|
- /* To handle GMAC own interrupts */
|
|
|
- priv->hw->mac->host_irq_status((void __iomem *) dev->base_addr);
|
|
|
+ /* To handle GMAC own interrupts */
|
|
|
+ if (priv->plat->has_gmac) {
|
|
|
+ int status = priv->hw->mac->host_irq_status((void __iomem *)
|
|
|
+ dev->base_addr);
|
|
|
+ if (unlikely(status)) {
|
|
|
+ if (status & core_mmc_tx_irq)
|
|
|
+ priv->xstats.mmc_tx_irq_n++;
|
|
|
+ if (status & core_mmc_rx_irq)
|
|
|
+ priv->xstats.mmc_rx_irq_n++;
|
|
|
+ if (status & core_mmc_rx_csum_offload_irq)
|
|
|
+ priv->xstats.mmc_rx_csum_offload_irq_n++;
|
|
|
+ if (status & core_irq_receive_pmt_irq)
|
|
|
+ priv->xstats.irq_receive_pmt_irq_n++;
|
|
|
+
|
|
|
+ /* For LPI we need to save the tx status */
|
|
|
+ if (status & core_irq_tx_path_in_lpi_mode) {
|
|
|
+ priv->xstats.irq_tx_path_in_lpi_mode_n++;
|
|
|
+ priv->tx_path_in_lpi_mode = true;
|
|
|
+ }
|
|
|
+ if (status & core_irq_tx_path_exit_lpi_mode) {
|
|
|
+ priv->xstats.irq_tx_path_exit_lpi_mode_n++;
|
|
|
+ priv->tx_path_in_lpi_mode = false;
|
|
|
+ }
|
|
|
+ if (status & core_irq_rx_path_in_lpi_mode)
|
|
|
+ priv->xstats.irq_rx_path_in_lpi_mode_n++;
|
|
|
+ if (status & core_irq_rx_path_exit_lpi_mode)
|
|
|
+ priv->xstats.irq_rx_path_exit_lpi_mode_n++;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
+ /* To handle DMA interrupts */
|
|
|
stmmac_dma_interrupt(priv);
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
@@ -2155,6 +2304,9 @@ static int __init stmmac_cmdline_opt(char *str)
|
|
|
} else if (!strncmp(opt, "pause:", 6)) {
|
|
|
if (kstrtoint(opt + 6, 0, &pause))
|
|
|
goto err;
|
|
|
+ } else if (!strncmp(opt, "eee_timer:", 6)) {
|
|
|
+ if (kstrtoint(opt + 10, 0, &eee_timer))
|
|
|
+ goto err;
|
|
|
#ifdef CONFIG_STMMAC_TIMER
|
|
|
} else if (!strncmp(opt, "tmrate:", 7)) {
|
|
|
if (kstrtoint(opt + 7, 0, &tmrate))
|