Received: by 2002:ac0:a5b6:0:0:0:0:0 with SMTP id m51-v6csp3996656imm; Tue, 29 May 2018 19:10:57 -0700 (PDT) X-Google-Smtp-Source: ADUXVKL9CSq+ZU32UKjQq+1XDsFlpwbr2F/zj2bNNKUh1aL8mz5gRffTPP1s1TrNO1e0Qx4kwbj4 X-Received: by 2002:a17:902:9a95:: with SMTP id w21-v6mr872207plp.168.1527646257810; Tue, 29 May 2018 19:10:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1527646257; cv=none; d=google.com; s=arc-20160816; b=B9A3CixZ2Iaaqbo6HWtkpugDn/vzis5/Hp+fyCUG/Nc5AqssQPaUGBtMQ9+lXAKpov meAVeiw/mqkL/Fqw7IecjhFDoQYb+26qa+JpeYl49LIVYNdEU5QnEMHbASuJlhQ7kQSc U95dlPg/tTyzZ4sKCnCzG1iTHXa3VQ0rlvJnV41SxmDGnfkMckjrHGRTO3vBEet1Asoq fqWlR+5MTmvTgUKDRnTlwQeMLYJ+Oe1aGbzGqYA2HMyS3DBdz688bRUwkh23eXGCXoZr lZJToKQ1fjiKnXk2Yui8DUJsCqst6FWTXeAOPepHLHHiAlBorkoAUWRepLyIHRhhanun 9LMQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:message-id:date:subject:to:from :dkim-signature:arc-authentication-results; bh=7dofSp+vJA2RfXBoj2X6ZLgjZY/7qKY8jNqLLZc7nNY=; b=sNu1uWwellBSS2hFm9PofNOtdiZVrHEDgSQvw8NLLLW+/XvnItZxdZCBDCeBgwIonL 6XWET5rBAJieFMEyr5H2qBrQrG2FTP+tJI93Ti9dXrQYsnAC86kGlOAQCyq0Hz4L2aLL ROkPo1LZQlbKjFOUXg1wxZOp1NMs6gaJGReDxHzskFQV/mXKRdMZa7AnJZkAGfFQf173 6j5xGBFaU4E0oUp7mDFW1+YDXpB2/HAtDSFpmfGSziEUimYWgXdrJA8AvTBXKx2bhqfz 3/XPgLdiwJahBVf7uV99gLJ+vy6mnNDfE62CCRGAZMk0gh5uqUCFkad4l2GZLL9czkXI aGtA== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@gmail.com header.s=20161025 header.b=XnNyFMEw; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=canonical.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id o1-v6si33122325plb.220.2018.05.29.19.10.42; Tue, 29 May 2018 19:10:57 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=fail header.i=@gmail.com header.s=20161025 header.b=XnNyFMEw; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=canonical.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S935931AbeE3CKP (ORCPT + 99 others); Tue, 29 May 2018 22:10:15 -0400 Received: from mail-pg0-f68.google.com ([74.125.83.68]:44329 "EHLO mail-pg0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S935643AbeE3CKN (ORCPT ); Tue, 29 May 2018 22:10:13 -0400 Received: by mail-pg0-f68.google.com with SMTP id p21-v6so7404595pgd.11; Tue, 29 May 2018 19:10:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:subject:date:message-id; bh=7dofSp+vJA2RfXBoj2X6ZLgjZY/7qKY8jNqLLZc7nNY=; b=XnNyFMEwwj3XkfvAq2wa3Wpptj5G9g0pyQ2NOlxqs+1Qp4Bne+3wXyv1ylpMV6YFCm 6upwvD6NK+YutDaq9Q6aXPe/1PJvxdPyrBXoS4IUjFas62Fgcu4WQ4wlQun1y2fzbWvL 9T1LwWTxoO/aQf/5CH2UiHcbwQxJNKn1hBcNST4hBsjeQGartXno9Li1X0d0uOtre+0X SRaQCZc4/zK1NiI8gZIiSCCg3YnzUCb9d3GQF8F+TkHc9NTzzZRmzuppnrvtkwu2TyAE 98CyW1n99yMDHa8h479gn4IFYvA+rVvDGSdDuXElOJAhkDZv5i2SJB2LW3P+pQcW2rHs QCpg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:subject:date:message-id; bh=7dofSp+vJA2RfXBoj2X6ZLgjZY/7qKY8jNqLLZc7nNY=; b=aYBgmFBbWc2RsuN/cL2piZ9iB++JqFvC8uVcC9WssmStYfBIezTdXRVnyfyCxY52Xm 1augWjU2T0iQYDc27Ce9rvrRT3snzZ6evAxvDsP0jCO0V70uTE9MC0A/afgMEORleNzm /wp0abjC4BghBcOuOGVBANCHyZr4qcYDl31BxSCrlvWyV+Hb2BARLkPmIUyUM8U/1HTP tg0OkghOhacCb5BN99yKi3SalgBTcIYNzBFEhlrV4wRWl1MCAGCF8hlRQcDTUqtVLc8z 7PtyZ3mCKhfbEVpCyoXPEEsLe1I2tFcPkGfkZ5IQGm4Uj62lc1iXzfZuBdSQEXPTMmVC wgtQ== X-Gm-Message-State: ALKqPweAQJGjJWb36au24CuUainuBiz1nShVFfEEsl/UGjPv9egtMvNr ssIkAckjl6OOLpBNeBcM7cU= X-Received: by 2002:a63:ab45:: with SMTP id k5-v6mr674431pgp.192.1527646212163; Tue, 29 May 2018 19:10:12 -0700 (PDT) Received: from localhost ([175.41.48.77]) by smtp.gmail.com with ESMTPSA id t14-v6sm72930890pfh.109.2018.05.29.19.10.09 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Tue, 29 May 2018 19:10:10 -0700 (PDT) From: AceLan Kao To: Jay Cliburn , Chris Snook , "David S . Miller" , Rakesh Pandit , netdev@vger.kernel.org, Emily Chien , Andrew Lunn , Johannes Berg , Johannes Stezenbach , linux-kernel@vger.kernel.org Subject: [PATCH v2] Revert "alx: remove WoL support" Date: Wed, 30 May 2018 10:10:08 +0800 Message-Id: <20180530021008.15080-1-acelan.kao@canonical.com> X-Mailer: git-send-email 2.17.0 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This reverts commit bc2bebe8de8ed4ba6482c9cc370b0dd72ffe8cd2. The WoL feature is a must to pass Energy Star 6.1 and above, the power consumption will be measured during S3 with WoL is enabled. Reverting "alx: remove WoL support", and will try to fix the unintentional wake up issue when WoL is enabled. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=61651 Signed-off-by: AceLan Kao --- drivers/net/ethernet/atheros/alx/ethtool.c | 36 +++++ drivers/net/ethernet/atheros/alx/hw.c | 154 ++++++++++++++++++++- drivers/net/ethernet/atheros/alx/hw.h | 5 + drivers/net/ethernet/atheros/alx/main.c | 142 +++++++++++++++++-- 4 files changed, 326 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/ethtool.c b/drivers/net/ethernet/atheros/alx/ethtool.c index 2f4eabf652e8..859e27236ce4 100644 --- a/drivers/net/ethernet/atheros/alx/ethtool.c +++ b/drivers/net/ethernet/atheros/alx/ethtool.c @@ -310,11 +310,47 @@ static int alx_get_sset_count(struct net_device *netdev, int sset) } } +static void alx_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct alx_priv *alx = netdev_priv(netdev); + struct alx_hw *hw = &alx->hw; + + wol->supported = WAKE_MAGIC | WAKE_PHY; + wol->wolopts = 0; + + if (hw->sleep_ctrl & ALX_SLEEP_WOL_MAGIC) + wol->wolopts |= WAKE_MAGIC; + if (hw->sleep_ctrl & ALX_SLEEP_WOL_PHY) + wol->wolopts |= WAKE_PHY; +} + +static int alx_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct alx_priv *alx = netdev_priv(netdev); + struct alx_hw *hw = &alx->hw; + + if (wol->wolopts & ~(WAKE_MAGIC | WAKE_PHY)) + return -EOPNOTSUPP; + + hw->sleep_ctrl = 0; + + if (wol->wolopts & WAKE_MAGIC) + hw->sleep_ctrl |= ALX_SLEEP_WOL_MAGIC; + if (wol->wolopts & WAKE_PHY) + hw->sleep_ctrl |= ALX_SLEEP_WOL_PHY; + + device_set_wakeup_enable(&alx->hw.pdev->dev, hw->sleep_ctrl); + + return 0; +} + const struct ethtool_ops alx_ethtool_ops = { .get_pauseparam = alx_get_pauseparam, .set_pauseparam = alx_set_pauseparam, .get_msglevel = alx_get_msglevel, .set_msglevel = alx_set_msglevel, + .get_wol = alx_get_wol, + .set_wol = alx_set_wol, .get_link = ethtool_op_get_link, .get_strings = alx_get_strings, .get_sset_count = alx_get_sset_count, diff --git a/drivers/net/ethernet/atheros/alx/hw.c b/drivers/net/ethernet/atheros/alx/hw.c index 6ac40b0003a3..f9bf612550ab 100644 --- a/drivers/net/ethernet/atheros/alx/hw.c +++ b/drivers/net/ethernet/atheros/alx/hw.c @@ -332,6 +332,16 @@ void alx_set_macaddr(struct alx_hw *hw, const u8 *addr) alx_write_mem32(hw, ALX_STAD1, val); } +static void alx_enable_osc(struct alx_hw *hw) +{ + u32 val; + + /* rising edge */ + val = alx_read_mem32(hw, ALX_MISC); + alx_write_mem32(hw, ALX_MISC, val & ~ALX_MISC_INTNLOSC_OPEN); + alx_write_mem32(hw, ALX_MISC, val | ALX_MISC_INTNLOSC_OPEN); +} + static void alx_reset_osc(struct alx_hw *hw, u8 rev) { u32 val, val2; @@ -774,7 +784,6 @@ int alx_setup_speed_duplex(struct alx_hw *hw, u32 ethadv, u8 flowctrl) return err; } - void alx_post_phy_link(struct alx_hw *hw) { u16 phy_val, len, agc; @@ -848,6 +857,65 @@ void alx_post_phy_link(struct alx_hw *hw) } } +/* NOTE: + * 1. phy link must be established before calling this function + * 2. wol option (pattern,magic,link,etc.) is configed before call it. + */ +int alx_pre_suspend(struct alx_hw *hw, int speed, u8 duplex) +{ + u32 master, mac, phy, val; + int err = 0; + + master = alx_read_mem32(hw, ALX_MASTER); + master &= ~ALX_MASTER_PCLKSEL_SRDS; + mac = hw->rx_ctrl; + /* 10/100 half */ + ALX_SET_FIELD(mac, ALX_MAC_CTRL_SPEED, ALX_MAC_CTRL_SPEED_10_100); + mac &= ~(ALX_MAC_CTRL_FULLD | ALX_MAC_CTRL_RX_EN | ALX_MAC_CTRL_TX_EN); + + phy = alx_read_mem32(hw, ALX_PHY_CTRL); + phy &= ~(ALX_PHY_CTRL_DSPRST_OUT | ALX_PHY_CTRL_CLS); + phy |= ALX_PHY_CTRL_RST_ANALOG | ALX_PHY_CTRL_HIB_PULSE | + ALX_PHY_CTRL_HIB_EN; + + /* without any activity */ + if (!(hw->sleep_ctrl & ALX_SLEEP_ACTIVE)) { + err = alx_write_phy_reg(hw, ALX_MII_IER, 0); + if (err) + return err; + phy |= ALX_PHY_CTRL_IDDQ | ALX_PHY_CTRL_POWER_DOWN; + } else { + if (hw->sleep_ctrl & (ALX_SLEEP_WOL_MAGIC | ALX_SLEEP_CIFS)) + mac |= ALX_MAC_CTRL_RX_EN | ALX_MAC_CTRL_BRD_EN; + if (hw->sleep_ctrl & ALX_SLEEP_CIFS) + mac |= ALX_MAC_CTRL_TX_EN; + if (duplex == DUPLEX_FULL) + mac |= ALX_MAC_CTRL_FULLD; + if (speed == SPEED_1000) + ALX_SET_FIELD(mac, ALX_MAC_CTRL_SPEED, + ALX_MAC_CTRL_SPEED_1000); + phy |= ALX_PHY_CTRL_DSPRST_OUT; + err = alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, + ALX_MIIEXT_S3DIG10, + ALX_MIIEXT_S3DIG10_SL); + if (err) + return err; + } + + alx_enable_osc(hw); + hw->rx_ctrl = mac; + alx_write_mem32(hw, ALX_MASTER, master); + alx_write_mem32(hw, ALX_MAC_CTRL, mac); + alx_write_mem32(hw, ALX_PHY_CTRL, phy); + + /* set val of PDLL D3PLLOFF */ + val = alx_read_mem32(hw, ALX_PDLL_TRNS1); + val |= ALX_PDLL_TRNS1_D3PLLOFF_EN; + alx_write_mem32(hw, ALX_PDLL_TRNS1, val); + + return 0; +} + bool alx_phy_configured(struct alx_hw *hw) { u32 cfg, hw_cfg; @@ -920,6 +988,26 @@ int alx_clear_phy_intr(struct alx_hw *hw) return alx_read_phy_reg(hw, ALX_MII_ISR, &isr); } +int alx_config_wol(struct alx_hw *hw) +{ + u32 wol = 0; + int err = 0; + + /* turn on magic packet event */ + if (hw->sleep_ctrl & ALX_SLEEP_WOL_MAGIC) + wol |= ALX_WOL0_MAGIC_EN | ALX_WOL0_PME_MAGIC_EN; + + /* turn on link up event */ + if (hw->sleep_ctrl & ALX_SLEEP_WOL_PHY) { + wol |= ALX_WOL0_LINK_EN | ALX_WOL0_PME_LINK; + /* only link up can wake up */ + err = alx_write_phy_reg(hw, ALX_MII_IER, ALX_IER_LINK_UP); + } + alx_write_mem32(hw, ALX_WOL0, wol); + + return err; +} + void alx_disable_rss(struct alx_hw *hw) { u32 ctrl = alx_read_mem32(hw, ALX_RXQ0); @@ -1044,6 +1132,70 @@ void alx_mask_msix(struct alx_hw *hw, int index, bool mask) alx_post_write(hw); } +int alx_select_powersaving_speed(struct alx_hw *hw, int *speed, u8 *duplex) +{ + int i, err; + u16 lpa; + + err = alx_read_phy_link(hw); + if (err) + return err; + + if (hw->link_speed == SPEED_UNKNOWN) { + *speed = SPEED_UNKNOWN; + *duplex = DUPLEX_UNKNOWN; + return 0; + } + + err = alx_read_phy_reg(hw, MII_LPA, &lpa); + if (err) + return err; + + if (!(lpa & LPA_LPACK)) { + *speed = hw->link_speed; + return 0; + } + + if (lpa & LPA_10FULL) { + *speed = SPEED_10; + *duplex = DUPLEX_FULL; + } else if (lpa & LPA_10HALF) { + *speed = SPEED_10; + *duplex = DUPLEX_HALF; + } else if (lpa & LPA_100FULL) { + *speed = SPEED_100; + *duplex = DUPLEX_FULL; + } else { + *speed = SPEED_100; + *duplex = DUPLEX_HALF; + } + + if (*speed == hw->link_speed && *duplex == hw->duplex) + return 0; + err = alx_write_phy_reg(hw, ALX_MII_IER, 0); + if (err) + return err; + err = alx_setup_speed_duplex(hw, alx_speed_to_ethadv(*speed, *duplex) | + ADVERTISED_Autoneg, ALX_FC_ANEG | + ALX_FC_RX | ALX_FC_TX); + if (err) + return err; + + /* wait for linkup */ + for (i = 0; i < ALX_MAX_SETUP_LNK_CYCLE; i++) { + msleep(100); + + err = alx_read_phy_link(hw); + if (err < 0) + return err; + if (hw->link_speed != SPEED_UNKNOWN) + break; + } + if (i == ALX_MAX_SETUP_LNK_CYCLE) + return -ETIMEDOUT; + + return 0; +} bool alx_get_phy_info(struct alx_hw *hw) { diff --git a/drivers/net/ethernet/atheros/alx/hw.h b/drivers/net/ethernet/atheros/alx/hw.h index e42d7e0947eb..a7fb6c8d846a 100644 --- a/drivers/net/ethernet/atheros/alx/hw.h +++ b/drivers/net/ethernet/atheros/alx/hw.h @@ -487,6 +487,8 @@ struct alx_hw { u8 flowctrl; u32 adv_cfg; + u32 sleep_ctrl; + spinlock_t mdio_lock; struct mdio_if_info mdio; u16 phy_id[2]; @@ -549,12 +551,14 @@ void alx_reset_pcie(struct alx_hw *hw); void alx_enable_aspm(struct alx_hw *hw, bool l0s_en, bool l1_en); int alx_setup_speed_duplex(struct alx_hw *hw, u32 ethadv, u8 flowctrl); void alx_post_phy_link(struct alx_hw *hw); +int alx_pre_suspend(struct alx_hw *hw, int speed, u8 duplex); int alx_read_phy_reg(struct alx_hw *hw, u16 reg, u16 *phy_data); int alx_write_phy_reg(struct alx_hw *hw, u16 reg, u16 phy_data); int alx_read_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 *pdata); int alx_write_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 data); int alx_read_phy_link(struct alx_hw *hw); int alx_clear_phy_intr(struct alx_hw *hw); +int alx_config_wol(struct alx_hw *hw); void alx_cfg_mac_flowcontrol(struct alx_hw *hw, u8 fc); void alx_start_mac(struct alx_hw *hw); int alx_reset_mac(struct alx_hw *hw); @@ -563,6 +567,7 @@ bool alx_phy_configured(struct alx_hw *hw); void alx_configure_basic(struct alx_hw *hw); void alx_mask_msix(struct alx_hw *hw, int index, bool mask); void alx_disable_rss(struct alx_hw *hw); +int alx_select_powersaving_speed(struct alx_hw *hw, int *speed, u8 *duplex); bool alx_get_phy_info(struct alx_hw *hw); void alx_update_hw_stats(struct alx_hw *hw); diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index 567ee54504bc..c0e2bb22ce24 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -1070,6 +1070,7 @@ static int alx_init_sw(struct alx_priv *alx) alx->dev->max_mtu = ALX_MAX_FRAME_LEN(ALX_MAX_FRAME_SIZE); alx->tx_ringsz = 256; alx->rx_ringsz = 512; + hw->sleep_ctrl = ALX_SLEEP_WOL_MAGIC | ALX_SLEEP_WOL_PHY; hw->imt = 200; alx->int_mask = ALX_ISR_MISC; hw->dma_chnl = hw->max_dma_chnl; @@ -1346,6 +1347,66 @@ static int alx_stop(struct net_device *netdev) return 0; } +static int __alx_shutdown(struct pci_dev *pdev, bool *wol_en) +{ + struct alx_priv *alx = pci_get_drvdata(pdev); + struct net_device *netdev = alx->dev; + struct alx_hw *hw = &alx->hw; + int err, speed; + u8 duplex; + + netif_device_detach(netdev); + + if (netif_running(netdev)) + __alx_stop(alx); + +#ifdef CONFIG_PM_SLEEP + err = pci_save_state(pdev); + if (err) + return err; +#endif + + err = alx_select_powersaving_speed(hw, &speed, &duplex); + if (err) + return err; + err = alx_clear_phy_intr(hw); + if (err) + return err; + err = alx_pre_suspend(hw, speed, duplex); + if (err) + return err; + err = alx_config_wol(hw); + if (err) + return err; + + *wol_en = false; + if (hw->sleep_ctrl & ALX_SLEEP_ACTIVE) { + netif_info(alx, wol, netdev, + "wol: ctrl=%X, speed=%X\n", + hw->sleep_ctrl, speed); + device_set_wakeup_enable(&pdev->dev, true); + *wol_en = true; + } + + pci_disable_device(pdev); + + return 0; +} + +static void alx_shutdown(struct pci_dev *pdev) +{ + int err; + bool wol_en; + + err = __alx_shutdown(pdev, &wol_en); + if (!err) { + pci_wake_from_d3(pdev, wol_en); + pci_set_power_state(pdev, PCI_D3hot); + } else { + dev_err(&pdev->dev, "shutdown fail %d\n", err); + } +} + static void alx_link_check(struct work_struct *work) { struct alx_priv *alx; @@ -1841,6 +1902,8 @@ static int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_unmap; } + device_set_wakeup_enable(&pdev->dev, hw->sleep_ctrl); + netdev_info(netdev, "Qualcomm Atheros AR816x/AR817x Ethernet [%pM]\n", netdev->dev_addr); @@ -1883,12 +1946,22 @@ static void alx_remove(struct pci_dev *pdev) static int alx_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); - struct alx_priv *alx = pci_get_drvdata(pdev); + int err; + bool wol_en; + + err = __alx_shutdown(pdev, &wol_en); + if (err) { + dev_err(&pdev->dev, "shutdown fail in suspend %d\n", err); + return err; + } + + if (wol_en) { + pci_prepare_to_sleep(pdev); + } else { + pci_wake_from_d3(pdev, false); + pci_set_power_state(pdev, PCI_D3hot); + } - if (!netif_running(alx->dev)) - return 0; - netif_device_detach(alx->dev); - __alx_stop(alx); return 0; } @@ -1896,23 +1969,69 @@ static int alx_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct alx_priv *alx = pci_get_drvdata(pdev); + struct net_device *netdev = alx->dev; struct alx_hw *hw = &alx->hw; + int err; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_save_state(pdev); + + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + hw->link_speed = SPEED_UNKNOWN; + alx->int_mask = ALX_ISR_MISC; + + alx_reset_pcie(hw); alx_reset_phy(hw); - if (!netif_running(alx->dev)) - return 0; - netif_device_attach(alx->dev); - return __alx_open(alx, true); + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_save_state(pdev); + + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + hw->link_speed = SPEED_UNKNOWN; + alx->int_mask = ALX_ISR_MISC; + + alx_reset_pcie(hw); + alx_reset_phy(hw); + + err = alx_reset_mac(hw); + if (err) { + netif_err(alx, hw, alx->dev, + "resume:reset_mac fail %d\n", err); + return -EIO; + } + + err = alx_setup_speed_duplex(hw, hw->adv_cfg, hw->flowctrl); + if (err) { + netif_err(alx, hw, alx->dev, + "resume:setup_speed_duplex fail %d\n", err); + return -EIO; + } + + if (netif_running(netdev)) { + err = __alx_open(alx, true); + if (err) + return err; + } + + netif_device_attach(netdev); + + return err; } +#endif +#ifdef CONFIG_PM_SLEEP static SIMPLE_DEV_PM_OPS(alx_pm_ops, alx_suspend, alx_resume); #define ALX_PM_OPS (&alx_pm_ops) #else #define ALX_PM_OPS NULL #endif - static pci_ers_result_t alx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { @@ -1955,6 +2074,8 @@ static pci_ers_result_t alx_pci_error_slot_reset(struct pci_dev *pdev) } pci_set_master(pdev); + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); alx_reset_pcie(hw); if (!alx_reset_mac(hw)) @@ -2011,6 +2132,7 @@ static struct pci_driver alx_driver = { .id_table = alx_pci_tbl, .probe = alx_probe, .remove = alx_remove, + .shutdown = alx_shutdown, .err_handler = &alx_err_handlers, .driver.pm = ALX_PM_OPS, }; -- 2.17.0