Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fd060e92 authored by Dilip Kota's avatar Dilip Kota
Browse files

msm: emac: Add Wake-on-LAN support



This change configures external PHY AT803X to
detect the magic packet during system suspend state.
On receiving magic packet external PHY will wakeup
the system from suspend state.

Change-Id: I9e86e7db2fee4248c35d96cf130c78b938a97aa5
Signed-off-by: default avatarDilip Kota <dkota@codeaurora.org>
parent 38afda62
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -760,5 +760,6 @@ int emac_clk_set_rate(struct emac_adapter *adpt, enum emac_clk_id id,
		      enum emac_clk_rate rate);
void emac_task_schedule(struct emac_adapter *adpt);
void emac_check_lsc(struct emac_adapter *adpt);
void emac_wol_gpio_irq(struct emac_adapter *adpt, bool enable);

#endif /* _QCOM_EMAC_H_ */
+15 −3
Original line number Diff line number Diff line
/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -322,6 +322,8 @@ static void emac_get_wol(struct net_device *netdev,
static int emac_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{
	struct emac_adapter *adpt = netdev_priv(netdev);
	struct phy_device *phydev = netdev->phydev;
	u32 ret = 0;

	if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE |
			    WAKE_UCAST | WAKE_BCAST | WAKE_MCAST))
@@ -330,13 +332,23 @@ static int emac_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
	if (emac_wol_exclusion(adpt, wol))
		return wol->wolopts ? -EOPNOTSUPP : 0;

	/* Enable WOL interrupt */
	ret = phy_ethtool_set_wol(phydev, wol);
	if (ret)
		return ret;

	adpt->wol = 0;
	if (wol->wolopts & WAKE_MAGIC)
	if (wol->wolopts & WAKE_MAGIC) {
		adpt->wol |= EMAC_WOL_MAGIC;
		emac_wol_gpio_irq(adpt, true);
		/* Release wakelock */
		__pm_relax(&adpt->link_wlock);
	}

	if (wol->wolopts & WAKE_PHY)
		adpt->wol |= EMAC_WOL_PHY;

	return 0;
	return ret;
}

static int emac_get_intr_coalesce(struct net_device *netdev,
+1 −0
Original line number Diff line number Diff line
@@ -149,4 +149,5 @@ bool emac_hw_read_tx_tstamp(struct emac_hw *hw, struct emac_hwtxtstamp *ts);
/* MII_INT_ENABLE/MII_INT_STATUS */
#define LINK_SUCCESS_INTERRUPT			BIT(10)
#define LINK_SUCCESS_BX			BIT(7)
#define WOL_INT				BIT(0)
#endif /*_EMAC_HW_H_*/
+17 −6
Original line number Diff line number Diff line
@@ -1132,7 +1132,7 @@ static int emac_start_xmit(struct sk_buff *skb,
}

/* This funciton aquire spin-lock so should not call from sleeping context */
static void emac_wol_gpio_irq(struct emac_adapter *adpt, bool enable)
void emac_wol_gpio_irq(struct emac_adapter *adpt, bool enable)
{
	struct emac_irq_per_dev *wol_irq = &adpt->irq[EMAC_WOL_IRQ];
	struct emac_phy *phy = &adpt->phy;
@@ -1174,6 +1174,8 @@ static irqreturn_t emac_wol_isr(int irq, void *data)
	if (!pm_runtime_status_suspended(adpt->netdev->dev.parent)) {
		if (val)
			emac_wol_gpio_irq(adpt, false);
		if (val & WOL_INT)
			__pm_stay_awake(&adpt->link_wlock);
	}
	return IRQ_HANDLED;
}
@@ -2035,9 +2037,10 @@ static int emac_open(struct net_device *netdev)
	if (irq->irq) {
		/* Register for EMAC WOL ISR */
		retval = request_threaded_irq(irq->irq, NULL, irq_cmn->handler,
					      IRQF_TRIGGER_FALLING
					      IRQF_TRIGGER_LOW
					      | IRQF_ONESHOT,
					      irq_cmn->name, irq);
		enable_irq_wake(irq->irq);
		if (retval) {
			emac_err(adpt,
				 "error:%d on request_irq(%d:%s flags:0x%lx)\n",
@@ -2076,6 +2079,7 @@ static int emac_close(struct net_device *netdev)
		phy->is_wol_enabled = false;
		free_irq(adpt->irq[EMAC_WOL_IRQ].irq, &adpt->irq[EMAC_WOL_IRQ]);
		phy->is_wol_irq_reg = 0;
		disable_irq_wake(adpt->irq[EMAC_WOL_IRQ].irq);
	}

	if (!TEST_FLAG(adpt, ADPT_STATE_DOWN))
@@ -2452,7 +2456,7 @@ static void emac_init_adapter(struct emac_adapter *adpt)

	/* others */
	hw->preamble = EMAC_PREAMBLE_DEF;
	adpt->wol = EMAC_WOL_MAGIC | EMAC_WOL_PHY;
	adpt->wol = EMAC_WOL_PHY;

	adpt->phy.is_ext_phy_connect = 0;
}
@@ -2812,7 +2816,7 @@ static int emac_pm_suspend(struct device *device, bool wol_enable)
	u32 wufc = adpt->wol;

	/* Check link state. Don't suspend if link is up */
	if (netif_carrier_ok(adpt->netdev))
	if (netif_carrier_ok(adpt->netdev) && !(adpt->wol & EMAC_WOL_MAGIC))
		return -EPERM;

	/* cannot suspend if WOL interrupt is not enabled */
@@ -2834,7 +2838,7 @@ static int emac_pm_suspend(struct device *device, bool wol_enable)
	flush_delayed_work(&adpt->phydev->state_queue);
	if (QCA8337_PHY_ID != adpt->phydev->phy_id)
		emac_hw_config_pow_save(hw, adpt->phydev->speed, !!wufc,
					!!(wufc & EMAC_WOL_MAGIC));
					!!(wufc & EMAC_WOL_PHY));

	if (!adpt->phydev->link && phy->is_wol_irq_reg) {
		int value, i;
@@ -2936,17 +2940,24 @@ static int emac_pm_sys_suspend(struct device *device)

	if (!pm_runtime_enabled(device) || !pm_runtime_suspended(device)) {
		emac_pm_suspend(device, false);

		/* Synchronize runtime-pm and system-pm states:
		 * at this point we are already suspended. However, the
		 * runtime-PM framework still thinks that we are active.
		 * The three calls below let the runtime-PM know that we are
		 * suspended already without re-invoking the suspend callback
		 */
		if (adpt->wol & EMAC_WOL_MAGIC) {
			pm_runtime_mark_last_busy(netdev->dev.parent);
			pm_runtime_put_autosuspend(netdev->dev.parent);
		}
		pm_runtime_disable(netdev->dev.parent);
		pm_runtime_set_suspended(netdev->dev.parent);
		pm_runtime_enable(netdev->dev.parent);
	}

		/* Clear the Magic packet flag */
		adpt->wol &= ~EMAC_WOL_MAGIC;
	}
	netif_device_detach(netdev);
	emac_disable_clks(adpt);
	emac_disable_regulator(adpt, EMAC_VREG1, EMAC_VREG2);