driver inclusion category: feature
--------------------------------
Implement the pci_driver suspend function to enable the device to sleep, and implement the resume function to enable the device to resume operation.
Signed-off-by: Frank Sae Frank.Sae@motor-comm.com --- .../net/ethernet/motorcomm/yt6801/yt6801_hw.c | 118 ++++++++++++++++++ .../ethernet/motorcomm/yt6801/yt6801_net.c | 28 +++++ .../ethernet/motorcomm/yt6801/yt6801_pci.c | 61 +++++++++ 3 files changed, 207 insertions(+)
diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c index bd3036625..25411f2dd 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_hw.c @@ -2424,6 +2424,56 @@ static void fxgmac_config_dma_bus(struct fxgmac_pdata *pdata) wr32_mac(pdata, val, DMA_SBMR); }
+static int fxgmac_pre_powerdown(struct fxgmac_pdata *pdata) +{ + u32 val = 0; + int ret; + + fxgmac_disable_rx(pdata); + + yt_dbg(pdata, "%s, phy and mac status update\n", __func__); + + if (device_may_wakeup(pdata->dev)) { + val = rd32_mem(pdata, EPHY_CTRL); + if (val & EPHY_CTRL_STA_LINKUP) { + ret = phy_speed_down(pdata->phydev, true); + if (ret < 0) { + yt_err(pdata, "%s, phy_speed_down err:%d\n", __func__, ret); + return ret; + } + + ret = phy_read_status(pdata->phydev); + if (ret < 0) { + yt_err(pdata, "%s, phy_read_status err:%d\n", + __func__, ret); + return ret; + } + + pdata->phy_speed = pdata->phydev->speed; + pdata->phy_duplex = pdata->phydev->duplex; + yt_dbg(pdata, "%s, speed :%d, duplex :%d\n", __func__, + pdata->phy_speed, pdata->phy_duplex); + + fxgmac_config_mac_speed(pdata); + } else { + yt_dbg(pdata, "%s link down, do nothing\n", __func__); + } + } + + /* After enable OOB_WOL from efuse, mac will loopcheck phy status, + * and lead to panic sometimes. So we should disable it from powerup, + * enable it from power down. + */ + val = rd32_mem(pdata, OOB_WOL_CTRL); + fxgmac_set_bits(&val, OOB_WOL_CTRL_DIS_POS, OOB_WOL_CTRL_DIS_LEN, 0); + wr32_mem(pdata, val, OOB_WOL_CTRL); + fsleep(2000); + + fxgmac_set_mac_address(pdata, pdata->mac_addr); + + return 0; +} + static int fxgmac_phy_clear_interrupt(struct fxgmac_pdata *pdata) { u32 stats_pre, stats; @@ -2458,6 +2508,70 @@ static int fxgmac_phy_clear_interrupt(struct fxgmac_pdata *pdata) return -ETIMEDOUT; }
+static void fxgmac_config_powerdown(struct fxgmac_pdata *pdata, + bool wol) +{ + u32 val = 0; + + /* Use default arp offloading feature */ + fxgmac_update_aoe_ipv4addr(pdata, (u8 *)NULL); + fxgmac_enable_arp_offload(pdata); + fxgmac_update_ns_offload_ipv6addr(pdata, FXGMAC_NS_IFA_GLOBAL_UNICAST); + fxgmac_update_ns_offload_ipv6addr(pdata, FXGMAC_NS_IFA_LOCAL_LINK); + fxgmac_enable_ns_offload(pdata); + fxgmac_enable_wake_packet_indication(pdata, 1); + + /* Enable MAC Rx TX */ + val = rd32_mac(pdata, MAC_CR); + fxgmac_set_bits(&val, MAC_CR_RE_POS, MAC_CR_RE_LEN, 1); + fxgmac_set_bits(&val, MAC_CR_TE_POS, MAC_CR_TE_LEN, 1); + wr32_mac(pdata, val, MAC_CR); + + val = rd32_mem(pdata, LPW_CTRL); + fxgmac_set_bits(&val, LPW_CTRL_ASPM_LPW_EN_POS, + LPW_CTRL_ASPM_LPW_EN_LEN, 1); + wr32_mem(pdata, val, LPW_CTRL); + + /* Set gmac power down */ + val = rd32_mac(pdata, MAC_PMT_STA); + fxgmac_set_bits(&val, MAC_PMT_STA_PWRDWN_POS, MAC_PMT_STA_PWRDWN_LEN, + 1); + wr32_mac(pdata, val, MAC_PMT_STA); + + val = rd32_mem(pdata, MGMT_SIGDET); + fxgmac_set_bits(&val, MGMT_SIGDET_POS, MGMT_SIGDET_LEN, + MGMT_SIGDET_55MV); + wr32_mem(pdata, val, MGMT_SIGDET); + fxgmac_phy_clear_interrupt(pdata); +} + +static void fxgmac_config_powerup(struct fxgmac_pdata *pdata) +{ + u32 val = 0; + + /* After enable OOB_WOL from efuse, mac will loopcheck phy status, + * and lead to panic sometimes. + * So we should disable it from powerup, enable it from power down. + */ + val = rd32_mem(pdata, OOB_WOL_CTRL); + fxgmac_set_bits(&val, OOB_WOL_CTRL_DIS_POS, OOB_WOL_CTRL_DIS_LEN, 1); + wr32_mem(pdata, val, OOB_WOL_CTRL); + + /* Clear wpi mode whether or not waked by WOL, write reset value */ + val = rd32_mem(pdata, MGMT_WPI_CTRL0); + fxgmac_set_bits(&val, MGMT_WPI_CTRL0_WPI_MODE_POS, + MGMT_WPI_CTRL0_WPI_MODE_LEN, 0); + wr32_mem(pdata, val, MGMT_WPI_CTRL0); + + /* Read pmt_status register to De-assert the pmt_intr_o */ + val = rd32_mac(pdata, MAC_PMT_STA); + /* whether or not waked up by WOL, write reset value */ + fxgmac_set_bits(&val, MAC_PMT_STA_PWRDWN_POS, MAC_PMT_STA_PWRDWN_LEN, + 0); + /* Write register to synchronized always-on block */ + wr32_mac(pdata, val, MAC_PMT_STA); +} + #define FXGMAC_WOL_WAIT_2_MS 2
static void fxgmac_config_wol_wait_time(struct fxgmac_pdata *pdata) @@ -3262,4 +3376,8 @@ void fxgmac_hw_ops_init(struct fxgmac_hw_ops *hw_ops) hw_ops->enable_wake_pattern = fxgmac_enable_wake_pattern; hw_ops->disable_wake_pattern = fxgmac_disable_wake_pattern;
+ /* Power Management */ + hw_ops->pre_power_down = fxgmac_pre_powerdown; + hw_ops->config_power_down = fxgmac_config_powerdown; + hw_ops->config_power_up = fxgmac_config_powerup; } diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c index 6a3d1073c..10c103e95 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_net.c @@ -986,6 +986,34 @@ static void fxgmac_restart_work(struct work_struct *work) rtnl_unlock(); }
+int fxgmac_net_powerup(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + int ret; + + /* Signal that we are up now */ + pdata->powerstate = 0; + if (__test_and_set_bit(FXGMAC_POWER_STATE_UP, &pdata->powerstate)) + return 0; /* do nothing if already up */ + + ret = fxgmac_start(pdata); + if (ret < 0) { + yt_err(pdata, "%s: fxgmac_start err: %d\n", __func__, ret); + return ret; + } + + /* Must call it after fxgmac_start,because it will be + * enable in fxgmac_start + */ + hw_ops->disable_arp_offload(pdata); + + if (netif_msg_drv(pdata)) + yt_dbg(pdata, "%s, powerstate :%ld.\n", __func__, + pdata->powerstate); + + return 0; +} + int fxgmac_config_wol(struct fxgmac_pdata *pdata, bool en) { struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; diff --git a/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c b/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c index b2cd75b5c..860b79d13 100644 --- a/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c +++ b/drivers/net/ethernet/motorcomm/yt6801/yt6801_pci.c @@ -104,6 +104,62 @@ static void fxgmac_shutdown(struct pci_dev *pcidev) (system_state == SYSTEM_POWER_OFF) ? 1 : 0); }
+static int fxgmac_suspend(struct device *device) +{ + struct fxgmac_pdata *pdata = dev_get_drvdata(device); + struct net_device *netdev = pdata->netdev; + int ret = 0; + + mutex_lock(&pdata->mutex); + if (pdata->dev_state != FXGMAC_DEV_START) + goto unlock; + + if (netif_running(netdev)) { + ret = __fxgmac_shutdown(to_pci_dev(device), NULL); + if (ret < 0) + goto unlock; + } + + pdata->dev_state = FXGMAC_DEV_SUSPEND; +unlock: + mutex_unlock(&pdata->mutex); + + return ret; +} + +static int fxgmac_resume(struct device *device) +{ + struct fxgmac_pdata *pdata = dev_get_drvdata(device); + struct net_device *netdev = pdata->netdev; + int ret = 0; + + mutex_lock(&pdata->mutex); + if (pdata->dev_state != FXGMAC_DEV_SUSPEND) + goto unlock; + + pdata->dev_state = FXGMAC_DEV_RESUME; + __clear_bit(FXGMAC_POWER_STATE_DOWN, &pdata->powerstate); + + rtnl_lock(); + if (netif_running(netdev)) { + ret = fxgmac_net_powerup(pdata); + if (ret < 0) { + dev_err(device, "%s, fxgmac_net_powerup err:%d\n", + __func__, ret); + goto unlock; + } + } + + netif_device_attach(netdev); + rtnl_unlock(); + + dev_dbg(device, "%s ok\n", __func__); +unlock: + mutex_unlock(&pdata->mutex); + + return ret; +} + #define MOTORCOMM_PCI_ID 0x1f0a #define YT6801_PCI_DEVICE_ID 0x6801
@@ -114,11 +170,16 @@ static const struct pci_device_id fxgmac_pci_tbl[] = {
MODULE_DEVICE_TABLE(pci, fxgmac_pci_tbl);
+static const struct dev_pm_ops fxgmac_pm_ops = { + SYSTEM_SLEEP_PM_OPS(fxgmac_suspend, fxgmac_resume) +}; + static struct pci_driver fxgmac_pci_driver = { .name = FXGMAC_DRV_NAME, .id_table = fxgmac_pci_tbl, .probe = fxgmac_probe, .remove = fxgmac_remove, + .driver.pm = pm_ptr(&fxgmac_pm_ops), .shutdown = fxgmac_shutdown, };