@@ -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;
}
@@ -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;
@@ -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,
};
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(+)