diff options
Diffstat (limited to 'abs/core/linux')
-rw-r--r-- | abs/core/linux/000-enable-alx-wol-4.9.patch | 475 | ||||
-rw-r--r-- | abs/core/linux/PKGBUILD | 10 | ||||
-rw-r--r-- | abs/core/linux/linux.install.pkg | 2 |
3 files changed, 484 insertions, 3 deletions
diff --git a/abs/core/linux/000-enable-alx-wol-4.9.patch b/abs/core/linux/000-enable-alx-wol-4.9.patch new file mode 100644 index 0000000..3428afd --- /dev/null +++ b/abs/core/linux/000-enable-alx-wol-4.9.patch @@ -0,0 +1,475 @@ +diff -u drivers/net/ethernet/atheros/alx/ethtool.c drivers/net/ethernet/atheros/alx/ethtool.c +--- drivers/net/ethernet/atheros/alx/ethtool.c 2016-12-11 20:17:54.000000000 +0100 ++++ drivers/net/ethernet/atheros/alx/ethtool.c 2016-12-13 13:36:36.464047098 +0100 +@@ -299,6 +299,40 @@ + } + } + ++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_settings = alx_get_settings, + .set_settings = alx_set_settings, +@@ -306,6 +340,8 @@ + .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 -u drivers/net/ethernet/atheros/alx/hw.c drivers/net/ethernet/atheros/alx/hw.c +--- drivers/net/ethernet/atheros/alx/hw.c 2016-12-11 20:17:54.000000000 +0100 ++++ drivers/net/ethernet/atheros/alx/hw.c 2016-12-13 13:36:36.464047098 +0100 +@@ -332,6 +332,16 @@ + 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 @@ + } + } + ++ ++/* 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 @@ + 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 @@ + } + + ++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; +diff -u drivers/net/ethernet/atheros/alx/hw.h drivers/net/ethernet/atheros/alx/hw.h +--- drivers/net/ethernet/atheros/alx/hw.h 2016-12-11 20:17:54.000000000 +0100 ++++ drivers/net/ethernet/atheros/alx/hw.h 2016-12-13 13:36:36.464047098 +0100 +@@ -488,6 +488,8 @@ + u8 flowctrl; + u32 adv_cfg; + ++ u32 sleep_ctrl; ++ + spinlock_t mdio_lock; + struct mdio_if_info mdio; + u16 phy_id[2]; +@@ -550,12 +552,14 @@ + 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); +@@ -564,6 +568,7 @@ + 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 -u drivers/net/ethernet/atheros/alx/main.c drivers/net/ethernet/atheros/alx/main.c +--- drivers/net/ethernet/atheros/alx/main.c 2016-12-11 20:17:54.000000000 +0100 ++++ drivers/net/ethernet/atheros/alx/main.c 2016-12-13 13:36:47.736407751 +0100 +@@ -894,6 +894,7 @@ + alx->rxbuf_size = ALX_MAX_FRAME_LEN(hw->mtu); + 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; +@@ -1150,6 +1151,65 @@ + __alx_stop(netdev_priv(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) + { +@@ -1637,6 +1697,8 @@ + 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); +@@ -1679,12 +1741,21 @@ + 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; + +- 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; ++ } ++ ++ if (wol_en) { ++ pci_prepare_to_sleep(pdev); ++ } else { ++ pci_wake_from_d3(pdev, false); ++ pci_set_power_state(pdev, PCI_D3hot); ++ } + return 0; + } + +@@ -1692,20 +1763,47 @@ + { + struct pci_dev *pdev = to_pci_dev(dev); + struct alx_priv *alx = pci_get_drvdata(pdev); +- struct alx_hw *hw = &alx->hw; +- +- alx_reset_phy(hw); ++ 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); ++ ++ 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; ++ } + +- if (!netif_running(alx->dev)) +- return 0; +- netif_device_attach(alx->dev); +- return __alx_open(alx, true); ++ netif_device_attach(netdev); ++ 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 + + +@@ -1751,6 +1849,8 @@ + } + + 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)) +@@ -1802,11 +1902,19 @@ + {} + }; + ++#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, + }; diff --git a/abs/core/linux/PKGBUILD b/abs/core/linux/PKGBUILD index f7a2962..914d8b2 100644 --- a/abs/core/linux/PKGBUILD +++ b/abs/core/linux/PKGBUILD @@ -4,7 +4,7 @@ pkgbase=linux #pkgbase=linux-custom _srcname=linux-4.9 -pkgver=4.9.64 +pkgver=4.9.73 pkgrel=1 arch=('x86_64') url="https://www.kernel.org/" @@ -19,15 +19,18 @@ source=(https://www.kernel.org/pub/linux/kernel/v4.x/${_srcname}.tar.{xz,sign} '90-linux.hook' # standard config files for mkinitcpio ramdisk linux.preset + # add WOL to alx + '000-enable-alx-wol-4.9.patch' change-default-console-loglevel.patch) # https://www.kernel.org/pub/linux/kernel/v4.x/sha256sums.asc sha256sums=('029098dcffab74875e086ae970e3828456838da6e0ba22ce3f64ef764f3d7f1a' 'SKIP' - 'e17b3219b2b9ebdeb180e509b3a1a6eb46d957d6a95c17f4e6b6b7199067949a' + '2af9c1806d1c4a6b90f5b105bdb6eedba8e0610712d0bab8a906bb015b2993f3' 'SKIP' '2bafc851b494310056bdc1a8bd3f5342f18c50a83594f5e5bf603406c5144ae4' '834bd254b56ab71d73f59b3221f056c72f559553c04718e350ab2a3e2991afe0' '1f036f7464da54ae510630f0edb69faa115287f86d9f17641197ffda8cfd49e0' + 'f8f6c9e9d82fac0ad1333a15418df81e2186e6a2e257aa670d1da57b4a15cafe' '1256b241cd477b265a3c2d64bdc19ffe3c9bbcee82ea3994c590c2c76e767d99') validpgpkeys=('ABAF11C65A2970B130ABE3C479BE3E4300411886' # Linus Torvalds <torvalds@linux-foundation.org> '647F28654894E3BD457199BE38DBBDC86092693E' # Greg Kroah-Hartman (Linux kernel stable release signing key) <greg@kroah.com> @@ -49,6 +52,9 @@ prepare() { # (relevant patch sent upstream: https://lkml.org/lkml/2011/7/26/227) patch -p1 -i "${srcdir}/change-default-console-loglevel.patch" + # add WOL to alx + patch -p0 -i "${srcdir}/000-enable-alx-wol-4.9.patch" + cat "${srcdir}/config" > ./.config if [ "${_kernelname}" != "" ]; then diff --git a/abs/core/linux/linux.install.pkg b/abs/core/linux/linux.install.pkg index c4ca093..9229cc6 100644 --- a/abs/core/linux/linux.install.pkg +++ b/abs/core/linux/linux.install.pkg @@ -2,7 +2,7 @@ # arg 2: the old package version KERNEL_NAME= -KERNEL_VERSION=4.9.64-1-ARCH +KERNEL_VERSION=4.9.73-1-ARCH post_install () { # updating module dependencies |