]> bbs.cooldavid.org Git - jme.git/blobdiff - jme.c
jme: Support WoL after shutdown
[jme.git] / jme.c
diff --git a/jme.c b/jme.c
index e3db53f47c777ad8ccbd59fcee22e3eb694e489d..0c7720817a5c286f9f8dbf27d0173a9be7d68554 100644 (file)
--- a/jme.c
+++ b/jme.c
@@ -3,6 +3,7 @@
  *
  * Copyright 2008 JMicron Technology Corporation
  * http://www.jmicron.com/
+ * Copyright (c) 2009 - 2010 Guo-Fu Tseng <cooldavid@cooldavid.org>
  *
  * Author: Guo-Fu Tseng <cooldavid@cooldavid.org>
  *
@@ -999,6 +1000,7 @@ jme_process_receive(struct jme_adapter *jme, int limit)
                        goto out;
                --limit;
 
+               rmb();
                desccnt = rxdesc->descwb.desccnt & RXWBDCNT_DCNT;
 
                if (unlikely(desccnt > 1 ||
@@ -1647,12 +1649,12 @@ err_out:
        return rc;
 }
 
-#ifdef CONFIG_PM
 static void
 jme_set_100m_half(struct jme_adapter *jme)
 {
        u32 bmcr, tmp;
 
+       jme_phy_on(jme);
        bmcr = jme_mdio_read(jme->dev, jme->mii_if.phy_id, MII_BMCR);
        tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
                       BMCR_SPEED1000 | BMCR_FULLDPLX);
@@ -1680,7 +1682,6 @@ jme_wait_link(struct jme_adapter *jme)
                phylink = jme_linkstat_from_phy(jme);
        }
 }
-#endif
 
 static inline void
 jme_phy_off(struct jme_adapter *jme)
@@ -1688,6 +1689,21 @@ jme_phy_off(struct jme_adapter *jme)
        jme_mdio_write(jme->dev, jme->mii_if.phy_id, MII_BMCR, BMCR_PDOWN);
 }
 
+static void
+jme_powersave_phy(struct jme_adapter *jme)
+{
+       if (jme->reg_pmcs) {
+               jme_set_100m_half(jme);
+
+               if (jme->reg_pmcs & (PMCS_LFEN | PMCS_LREN))
+                       jme_wait_link(jme);
+
+               jwrite32(jme, JME_PMCS, jme->reg_pmcs);
+       } else {
+               jme_phy_off(jme);
+       }
+}
+
 static int
 jme_close(struct net_device *netdev)
 {
@@ -2509,8 +2525,37 @@ jme_set_settings(struct net_device *netdev,
        if (!rc) {
                if (fdc)
                        jme_reset_link(jme);
-               set_bit(JME_FLAG_SSET, &jme->flags);
                jme->old_ecmd = *ecmd;
+               set_bit(JME_FLAG_SSET, &jme->flags);
+       }
+
+       return rc;
+}
+
+static int
+jme_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
+{
+       int rc;
+       struct jme_adapter *jme = netdev_priv(netdev);
+       struct mii_ioctl_data *mii_data = if_mii(rq);
+       unsigned int duplex_chg;
+
+       if (cmd == SIOCSMIIREG) {
+               u16 val = mii_data->val_in;
+               if (!(val & (BMCR_RESET|BMCR_ANENABLE)) &&
+                   (val & BMCR_SPEED1000))
+                       return -EINVAL;
+       }
+
+       spin_lock_bh(&jme->phy_lock);
+       rc = generic_mii_ioctl(&jme->mii_if, mii_data, cmd, &duplex_chg);
+       spin_unlock_bh(&jme->phy_lock);
+
+       if (!rc && (cmd == SIOCSMIIREG)) {
+               if (duplex_chg)
+                       jme_reset_link(jme);
+               jme_get_settings(netdev, &jme->old_ecmd);
+               set_bit(JME_FLAG_SSET, &jme->flags);
        }
 
        return rc;
@@ -2826,6 +2871,7 @@ static const struct net_device_ops jme_netdev_ops = {
        .ndo_open               = jme_open,
        .ndo_stop               = jme_close,
        .ndo_validate_addr      = eth_validate_addr,
+       .ndo_do_ioctl           = jme_ioctl,
        .ndo_start_xmit         = jme_start_xmit,
        .ndo_set_mac_address    = jme_set_macaddr,
        .ndo_set_multicast_list = jme_set_multi,
@@ -2889,6 +2935,7 @@ jme_init_one(struct pci_dev *pdev,
 #else
        netdev->open                    = jme_open;
        netdev->stop                    = jme_close;
+       netdev->do_ioctl                = jme_ioctl;
        netdev->hard_start_xmit         = jme_start_xmit;
        netdev->set_mac_address         = jme_set_macaddr;
        netdev->set_multicast_list      = jme_set_multi;
@@ -3035,6 +3082,8 @@ jme_init_one(struct pci_dev *pdev,
                jme->mii_if.supports_gmii = true;
        else
                jme->mii_if.supports_gmii = false;
+       jme->mii_if.phy_id_mask = 0x1F;
+       jme->mii_if.reg_num_mask = 0x1F;
        jme->mii_if.mdio_read = jme_mdio_read;
        jme->mii_if.mdio_write = jme_mdio_write;
 
@@ -3117,6 +3166,20 @@ jme_remove_one(struct pci_dev *pdev)
 
 }
 
+static void
+jme_shutdown(struct pci_dev *pdev)
+{
+       struct net_device *netdev = pci_get_drvdata(pdev);
+       struct jme_adapter *jme = netdev_priv(netdev);
+
+       jme_powersave_phy(jme);
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27)
+       pci_enable_wake(pdev, PCI_D3hot, true);
+#else
+       pci_pme_active(pdev, true);
+#endif
+}
+
 #ifdef CONFIG_PM
 static int
 jme_suspend(struct pci_dev *pdev, pm_message_t state)
@@ -3154,19 +3217,9 @@ jme_suspend(struct pci_dev *pdev, pm_message_t state)
        tasklet_hi_enable(&jme->rxempty_task);
 
        pci_save_state(pdev);
-       if (jme->reg_pmcs) {
-               jme_set_100m_half(jme);
-
-               if (jme->reg_pmcs & (PMCS_LFEN | PMCS_LREN))
-                       jme_wait_link(jme);
-
-               jwrite32(jme, JME_PMCS, jme->reg_pmcs);
-
-               pci_enable_wake(pdev, PCI_D3cold, true);
-       } else {
-               jme_phy_off(jme);
-       }
-       pci_set_power_state(pdev, PCI_D3cold);
+       jme_powersave_phy(jme);
+       pci_enable_wake(pdev, PCI_D3hot, true);
+       pci_set_power_state(pdev, PCI_D3hot);
 
        return 0;
 }
@@ -3217,6 +3270,7 @@ static struct pci_driver jme_driver = {
        .suspend        = jme_suspend,
        .resume         = jme_resume,
 #endif /* CONFIG_PM */
+       .shutdown       = jme_shutdown,
 };
 
 static int __init