/* * * If you received this File from Marvell, you may opt to use, redistribute and/or * modify this File in accordance with the terms and conditions of the General * Public License Version 2, June 1991 (the "GPL License"), a copy of which is * available along with the File in the license.txt file or by writing to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY * DISCLAIMED. The GPL License provides additional details about this warranty * disclaimer. * */ #ifdef CONFIG_PM #include "oak_dpm.h" #include "oak_net.h" /* Name : oak_dpm_set_power_state * Returns : void * Parameters : struct device *dev, pci_power_t state * Description : This function set the device power state */ void oak_dpm_set_power_state(struct device *dev, pci_power_t state) { int retval; pci_power_t D0 = 0; struct net_device *ndev = dev_get_drvdata(dev); oak_t *np = netdev_priv(ndev); /* If user input state is D0 and current_state not D0 * i.e. current state is D3hot (D1, D2, D3) then * we call resume operation */ if (state == PCI_D0 && np->pdev->current_state != D0) { retval = oak_dpm_resume(dev); if (retval) pr_info("oak_dpm_resume: failed.\n"); } /* If user input state is D1 or D2 or D3 and current_state D0 * then we do the suspend operation */ else if (((state == PCI_D1) || (state == PCI_D2) || (state == PCI_D3hot)) && (np->pdev->current_state == D0)) { retval = oak_dpm_suspend(dev); if (retval != 0) pr_info("oak_dpm_suspend: failed\n"); } } /* Name : oak_dpm_state_store * Returns : ssize_t * Parameters : struct device *dev, struct device_attribute *attr, * const char *buf, size_t count * Description : This function store the sysfs entry */ static ssize_t oak_dpm_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { pci_power_t pwr; bool d0, d1, d2, d3; /* Validate user input */ d0 = sysfs_streq(buf, "D0"); d1 = sysfs_streq(buf, "D1"); d2 = sysfs_streq(buf, "D2"); d3 = sysfs_streq(buf, "D3"); /* D0 as input, set device as D0 * D1, D2, and D3 as input, set the device as D3 * For any other input, error message is triggered. */ if (d0) { pwr = PCI_D0; oak_dpm_set_power_state(dev, pwr); } else if (d1 || d2 || d3) { pwr = PCI_D3hot; oak_dpm_set_power_state(dev, pwr); } else { pr_err("oak: Wrong input, Device power states are D0, D1, D2 or D3\n"); } /* With the current sysfs implementation the kobject reference count is * only modified directly by the function sysfs_schedule_callback(). */ return count; } /* The sysfs kernel object device attribute file oak_dpm_state * is wrtite only. Hence only oak_dpm_state_store function is * called by kernel when a user does input to oak_dpm_state file */ static DEVICE_ATTR_WO(oak_dpm_state); /* Name : oak_dpm_create_sysfs * Returns : void * Parameters : oak_t *np * Description : This function creates sysfs entry for setting device power * states D0, D1, D2 and D3. */ void oak_dpm_create_sysfs(oak_t *np) { int status; status = sysfs_create_file(&np->pdev->dev.kobj, &dev_attr_oak_dpm_state.attr); if (status) pr_err("oak: Failed to create sysfs entry\n"); } /* Name : oak_dpm_remove_sysfs * Returns : void * Parameters : oak_t *np * Description : This function removes sysfs entry of device power states */ void oak_dpm_remove_sysfs(oak_t *np) { sysfs_remove_file(&np->pdev->dev.kobj, &dev_attr_oak_dpm_state.attr); } #ifdef CONFIG_PM_SLEEP /* Name : oak_dpm_suspend * Returns : int * Parameters : struct device *dev * Description : This function is called when system goes into suspend state * and put the device into sleep state */ int __maybe_unused oak_dpm_suspend(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); oak_t *np = netdev_priv(ndev); int retval = 0; /* If interface is up then, do driver specific operations * gracefully close the interface */ retval = netif_running(ndev); if (retval) oak_net_close(ndev); /* Inform to PCI core, wake me up from D3hot when event triggers */ pci_enable_wake(np->pdev, PCI_D3hot, true); /* Release the oak hardware */ oak_release_hardware(np->pdev); /* To synchronize changes hold rtnl lock */ rtnl_lock(); /* Save the PCI state and prepare to go for sleep */ pci_save_state(np->pdev); pci_prepare_to_sleep(np->pdev); /* Set the device power state as D3hot */ retval = pci_set_power_state(np->pdev, PCI_D3hot); if (retval == 0) pr_info("%s: dpm state=D%d\n", __func__, np->pdev->current_state); else pr_err("%s: Failed to set the device power state err: %d\n", __func__, retval); /* Release the rtnl lock */ rtnl_unlock(); return retval; } /* Name : oak_dpm_resume * Returns : int * Parameters : struct device *dev * Description : This function called when system goes into resume state and put * the device into active state */ int __maybe_unused oak_dpm_resume(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); oak_t *np = netdev_priv(ndev); int retval = 0; /* To synchronize changes hold rtnl lock */ rtnl_lock(); /* Set the device power state as D0 */ retval = pci_set_power_state(np->pdev, PCI_D0); if (retval == 0) pr_info("%s: dpm state=D%d\n", __func__, np->pdev->current_state); /* Restore the PCI state */ pci_restore_state(np->pdev); /* The driver specific operations * Initialize the oak hardware * If interface is up, then call oak net open */ retval = oak_init_hardware(np->pdev); if (retval) { pr_err("%s: oak init hardware Not Successful %d\n", __func__, retval); } else { retval = netif_running(ndev); if (retval) { retval = oak_net_open(ndev); if (retval) pr_err("%s: oak net open failed\n", __func__); } } /* Release the rtnl lock */ rtnl_unlock(); return retval; } #endif #endif