123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498 |
- --- a/drivers/net/ethernet/atheros/alx/ethtool.c
- +++ b/drivers/net/ethernet/atheros/alx/ethtool.c
- @@ -321,11 +321,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,
- --- 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;
- @@ -848,6 +858,66 @@ 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 +990,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);
- @@ -1045,6 +1135,71 @@ void alx_mask_msix(struct alx_hw *hw, int index, bool mask)
- }
-
-
- +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)
- {
- u16 devs1, devs2;
- --- 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);
-
- --- a/drivers/net/ethernet/atheros/alx/main.c
- +++ b/drivers/net/ethernet/atheros/alx/main.c
- @@ -1069,6 +1069,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;
- @@ -1181,11 +1182,8 @@ static int alx_change_mtu(struct net_device *netdev, int mtu)
- alx->hw.mtu = mtu;
- alx->rxbuf_size = max(max_frame, ALX_DEF_RXBUF_SIZE);
- netdev_update_features(netdev);
- - if (netif_running(netdev)) {
- - mutex_lock(&alx->mtx);
- + if (netif_running(netdev))
- alx_reinit(alx);
- - mutex_unlock(&alx->mtx);
- - }
- return 0;
- }
-
- @@ -1371,6 +1369,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;
- @@ -1865,6 +1923,7 @@ 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);
- @@ -1910,16 +1969,26 @@ static int alx_suspend(struct device *dev)
- {
- struct alx_priv *alx = dev_get_drvdata(dev);
-
- - if (!netif_running(alx->dev))
- - return 0;
- + struct pci_dev *pdev = alx->hw.pdev;
- + int err;
- + bool wol_en;
-
- - rtnl_lock();
- - netif_device_detach(alx->dev);
- + //if (!netif_running(alx->dev))
- + // return 0;
- + //netif_device_detach(alx->dev);
- + //__alx_stop(alx);
- + err = __alx_shutdown(pdev, &wol_en);
- + if (err) {
- + dev_err(&pdev->dev, "shutdown fail in suspend %d\n", err);
- + return err;
- + }
-
- - mutex_lock(&alx->mtx);
- - __alx_stop(alx);
- - mutex_unlock(&alx->mtx);
- - rtnl_unlock();
- + if (wol_en) {
- + pci_prepare_to_sleep(pdev);
- + } else {
- + pci_wake_from_d3(pdev, false);
- + pci_set_power_state(pdev, PCI_D3hot);
- + }
-
- return 0;
- }
- @@ -1927,34 +1996,53 @@ static int alx_suspend(struct device *dev)
- static int alx_resume(struct device *dev)
- {
- struct alx_priv *alx = dev_get_drvdata(dev);
- + struct net_device *netdev = alx->dev;
- struct alx_hw *hw = &alx->hw;
- + struct pci_dev *pdev = hw->pdev;
- int err;
-
- - rtnl_lock();
- mutex_lock(&alx->mtx);
- +
- + 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)) {
- - err = 0;
- - goto unlock;
- + //if (!netif_running(alx->dev)) {
- + // err = 0;
- + // goto unlock;
- + //}
- + err = alx_reset_mac(hw);
- + if (err) {
- + netif_err(alx, hw, alx->dev, "resume:reset_mac fail %d\n", err);
- + err = -EIO;
- + goto unlock;
- }
-
- - err = __alx_open(alx, true);
- - if (err)
- - goto unlock;
- + //err = __alx_open(alx, true);
- + //if (err)
- + // goto unlock;
- + if (netif_running(netdev)) {
- + err = __alx_open(alx, true);
- + if (err)
- + goto unlock;
- + }
-
- netif_device_attach(alx->dev);
-
- unlock:
- mutex_unlock(&alx->mtx);
- - rtnl_unlock();
- return err;
- }
-
- -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
-
-
- @@ -2000,6 +2088,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))
- @@ -2049,11 +2139,20 @@ static const struct pci_device_id alx_pci_tbl[] = {
- {}
- };
-
- +#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 struct pci_driver alx_driver = {
- .name = alx_drv_name,
- .id_table = alx_pci_tbl,
- .probe = alx_probe,
- .remove = alx_remove,
- + .shutdown = alx_shutdown,
- .err_handler = &alx_err_handlers,
- .driver.pm = ALX_PM_OPS,
- };
|