Loading drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth.c +18 −1 Original line number Diff line number Diff line Loading @@ -170,6 +170,14 @@ static int ipa_eth_start_device(struct ipa_eth_device *eth_dev) return rc; } rc = ipa_eth_bus_disable_pc(eth_dev); if (rc) { ipa_eth_dev_err(eth_dev, "Failed to disable bus power collapse"); eth_dev->of_state = IPA_ETH_OF_ST_ERROR; return rc; } ipa_eth_dev_log(eth_dev, "Started device"); eth_dev->of_state = IPA_ETH_OF_ST_STARTED; Loading @@ -187,6 +195,14 @@ static int ipa_eth_stop_device(struct ipa_eth_device *eth_dev) if (eth_dev->of_state != IPA_ETH_OF_ST_STARTED) return -EFAULT; rc = ipa_eth_bus_enable_pc(eth_dev); if (rc) { ipa_eth_dev_err(eth_dev, "Failed to enable bus power collapse"); eth_dev->of_state = IPA_ETH_OF_ST_ERROR; return rc; } rc = ipa_eth_offload_stop(eth_dev); if (rc) { ipa_eth_dev_err(eth_dev, "Failed to stop offload"); Loading Loading @@ -987,6 +1003,7 @@ static void ipa_eth_ipc_log_cleanup(void) int ipa_eth_init(void) { int rc; unsigned int wq_flags = WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE; rc = ipa_eth_ipc_log_init(); if (rc) { Loading @@ -1002,7 +1019,7 @@ int ipa_eth_init(void) goto err_dbgfs; } ipa_eth_wq = alloc_workqueue("ipa_eth", WQ_UNBOUND, 0); ipa_eth_wq = alloc_workqueue("ipa_eth", wq_flags, 0); if (!ipa_eth_wq) { ipa_eth_err("Failed to alloc workqueue"); goto err_wq; Loading drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_bus.c +37 −0 Original line number Diff line number Diff line Loading @@ -87,9 +87,46 @@ void ipa_eth_bus_unregister_driver(struct ipa_eth_net_driver *nd) { struct ipa_eth_bus *eth_bus = lookup_eth_bus(nd->bus); if (!eth_bus) { ipa_eth_bug("Failed to lookup eth_bus for %s", nd->bus->name); return; } eth_bus->unregister_net_driver(nd); } int ipa_eth_bus_enable_pc(struct ipa_eth_device *eth_dev) { struct ipa_eth_net_driver *nd = eth_dev->nd; struct ipa_eth_bus *eth_bus = lookup_eth_bus(nd->bus); if (!eth_bus) { ipa_eth_dev_bug(eth_dev, "Failed to lookup eth_bus"); return -EFAULT; } if (eth_bus->enable_pc) return eth_bus->enable_pc(eth_dev); return -EFAULT; } int ipa_eth_bus_disable_pc(struct ipa_eth_device *eth_dev) { struct ipa_eth_net_driver *nd = eth_dev->nd; struct ipa_eth_bus *eth_bus = lookup_eth_bus(nd->bus); if (!eth_bus) { ipa_eth_dev_bug(eth_dev, "Failed to lookup eth_bus"); return -EFAULT; } if (eth_bus->disable_pc) return eth_bus->disable_pc(eth_dev); return -EFAULT; } int ipa_eth_bus_modinit(struct dentry *dbgfs_root) { int rc; Loading drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_i.h +6 −0 Original line number Diff line number Diff line Loading @@ -90,6 +90,9 @@ struct ipa_eth_bus { int (*register_net_driver)(struct ipa_eth_net_driver *nd); void (*unregister_net_driver)(struct ipa_eth_net_driver *nd); int (*enable_pc)(struct ipa_eth_device *eth_dev); int (*disable_pc)(struct ipa_eth_device *eth_dev); }; extern struct ipa_eth_bus ipa_eth_pci_bus; Loading @@ -112,6 +115,9 @@ void ipa_eth_bus_modexit(void); int ipa_eth_bus_register_driver(struct ipa_eth_net_driver *nd); void ipa_eth_bus_unregister_driver(struct ipa_eth_net_driver *nd); int ipa_eth_bus_enable_pc(struct ipa_eth_device *eth_dev); int ipa_eth_bus_disable_pc(struct ipa_eth_device *eth_dev); int ipa_eth_offload_modinit(struct dentry *dbgfs_root); void ipa_eth_offload_modexit(void); Loading drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_pci.c +168 −7 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #include <linux/debugfs.h> #include <linux/pci.h> #include <linux/msm_pcie.h> #include "ipa_eth_i.h" Loading @@ -25,6 +26,8 @@ struct ipa_eth_pci_driver { int (*probe_real)(struct pci_dev *dev, const struct pci_device_id *id); void (*remove_real)(struct pci_dev *dev); const struct dev_pm_ops *pm_ops_real; }; static LIST_HEAD(pci_drivers); Loading Loading @@ -127,7 +130,7 @@ static int ipa_eth_pci_probe_handler(struct pci_dev *pdev, rc = epci_drv->probe_real(pdev, id); if (rc) { ipa_eth_dev_err(eth_dev, "Failed real PCI probe of devfn=%u"); ipa_eth_err("Failed real PCI probe of devfn=%u"); goto err_probe; } Loading Loading @@ -172,6 +175,10 @@ static void ipa_eth_pci_remove_handler(struct pci_dev *pdev) pdev->driver->name, pdev->devfn); eth_dev = lookup_eth_dev(pdev); if (!eth_dev) { ipa_eth_bug("Failed to lookup pci_dev -> eth_dev"); return; } mutex_lock(&pci_devices_mutex); list_del(ð_dev->bus_device_list); Loading @@ -186,19 +193,113 @@ static void ipa_eth_pci_remove_handler(struct pci_dev *pdev) devm_kfree(dev, eth_dev); } static int ipa_eth_pci_suspend_handler(struct device *dev) { int rc = 0; struct ipa_eth_device *eth_dev; const struct dev_pm_ops *pm_ops_real; struct pci_dev *pci_dev = to_pci_dev(dev); eth_dev = lookup_eth_dev(pci_dev); if (!eth_dev) { ipa_eth_bug("Failed to lookup pci_dev -> eth_dev"); return -EFAULT; } pm_ops_real = ((struct ipa_eth_pci_driver *)eth_dev->bus_priv)->pm_ops_real; /* When offload is started, PCI power collapse is already disabled by * the ipa_eth_pci_disable_pc() api. Nonetheless, we still need to do * a dummy PCI config space save so that the PCIe framework will not by * itself perform a config space save-restore. */ if (eth_dev->of_state == IPA_ETH_OF_ST_STARTED) { ipa_eth_dev_log(eth_dev, "Device suspend performing dummy config space save"); rc = pci_save_state(pci_dev); } else { ipa_eth_dev_log(eth_dev, "Device suspend delegated to net driver"); rc = pm_ops_real->suspend(dev); } if (rc) ipa_eth_dev_log(eth_dev, "Device suspend failed"); else ipa_eth_dev_log(eth_dev, "Device suspend complete"); return rc; } static int ipa_eth_pci_resume_handler(struct device *dev) { int rc = 0; struct ipa_eth_device *eth_dev; const struct dev_pm_ops *pm_ops_real; struct pci_dev *pci_dev = to_pci_dev(dev); eth_dev = lookup_eth_dev(pci_dev); if (!eth_dev) { ipa_eth_bug("Failed to lookup pci_dev -> eth_dev"); return -EFAULT; } pm_ops_real = ((struct ipa_eth_pci_driver *)eth_dev->bus_priv)->pm_ops_real; /* During suspend, RC power collapse would not have happened if offload * was started. Ignore resume callback since the device does not need * to be re-initialized. */ if (eth_dev->of_state == IPA_ETH_OF_ST_STARTED) { ipa_eth_dev_log(eth_dev, "Device resume performing nop"); rc = 0; } else { ipa_eth_dev_log(eth_dev, "Device resume delegated to net driver"); rc = pm_ops_real->resume(dev); } if (rc) ipa_eth_dev_log(eth_dev, "Device resume failed"); else ipa_eth_dev_log(eth_dev, "Device resume complete"); return 0; } /* MSM PCIe driver invokes only suspend and resume callbacks, other operations * can be ignored. */ static const struct dev_pm_ops ipa_eth_pci_pm_ops = { .suspend = ipa_eth_pci_suspend_handler, .resume = ipa_eth_pci_resume_handler, }; static int ipa_eth_pci_register_net_driver(struct ipa_eth_net_driver *nd) { struct ipa_eth_pci_driver *epci_drv = NULL; struct pci_driver *pci_drv = container_of(nd->driver, struct pci_driver, driver); struct pci_driver *pci_drv; struct ipa_eth_pci_driver *epci_drv; if (!nd) { ipa_eth_err("Network driver is NULL"); return -EINVAL; } pci_drv = to_pci_driver(nd->driver); if (WARN_ON(!pci_drv->probe || !pci_drv->remove)) { ipa_eth_err("PCI driver lacking probe/remove callbacks"); ipa_eth_err("PCI net driver %s lacking probe/remove callbacks", nd->name); return -EFAULT; } if (!pci_drv->driver.pm || !pci_drv->driver.pm->suspend || !pci_drv->driver.pm->resume) { ipa_eth_err("PCI net driver %s does not support PM ops", nd->name); return -EFAULT; } Loading @@ -212,6 +313,9 @@ static int ipa_eth_pci_register_net_driver(struct ipa_eth_net_driver *nd) epci_drv->remove_real = pci_drv->remove; pci_drv->remove = ipa_eth_pci_remove_handler; epci_drv->pm_ops_real = pci_drv->driver.pm; pci_drv->driver.pm = &ipa_eth_pci_pm_ops; epci_drv->nd = nd; mutex_lock(&pci_drivers_mutex); Loading @@ -224,8 +328,7 @@ static int ipa_eth_pci_register_net_driver(struct ipa_eth_net_driver *nd) static void ipa_eth_pci_unregister_net_driver(struct ipa_eth_net_driver *nd) { struct pci_driver *pci_drv = container_of(nd->driver, struct pci_driver, driver); struct pci_driver *pci_drv = to_pci_driver(nd->driver); struct ipa_eth_pci_driver *epci_drv = lookup_epci_driver(pci_drv); mutex_lock(&pci_drivers_mutex); Loading @@ -235,14 +338,72 @@ static void ipa_eth_pci_unregister_net_driver(struct ipa_eth_net_driver *nd) pci_drv->probe = epci_drv->probe_real; pci_drv->remove = epci_drv->remove_real; pci_drv->driver.pm = epci_drv->pm_ops_real; memset(epci_drv, 0, sizeof(*epci_drv)); kfree(epci_drv); } /** * ipa_eth_pci_enable_pc() - Permit power collapse of the PCI root port * @eth_dev: Device attached to the PCI bus * * This function instructs the MSM PCIe bus driver to permit power collapse * of the root complex when Linux goes to suspend state. * * Return: 0 on success, non-zero otherwise */ static int ipa_eth_pci_enable_pc(struct ipa_eth_device *eth_dev) { int rc; struct pci_dev *pci_dev = to_pci_dev(eth_dev->dev); rc = msm_pcie_pm_control(MSM_PCIE_ENABLE_PC, pci_dev->bus->number, pci_dev, NULL, MSM_PCIE_CONFIG_INVALID); if (rc) { ipa_eth_dev_err(eth_dev, "Failed to enable MSM PCIe power collapse"); } else { ipa_eth_dev_log(eth_dev, "Enabled MSM PCIe power collapse"); } return rc; } /** * ipa_eth_pci_disable_pc() - Prevent power collapse of the PCI root port * @eth_dev: Device attached to the PCI bus * * This function instructs the MSM PCIe bus driver to prevent power collapse * of the root complex and connected EPs when Linux goes to suspend state. * * Return: 0 on success, non-zero otherwise */ static int ipa_eth_pci_disable_pc(struct ipa_eth_device *eth_dev) { int rc; struct pci_dev *pci_dev = to_pci_dev(eth_dev->dev); rc = msm_pcie_pm_control(MSM_PCIE_DISABLE_PC, pci_dev->bus->number, pci_dev, NULL, MSM_PCIE_CONFIG_INVALID); if (rc) { ipa_eth_dev_err(eth_dev, "Failed to disable MSM PCIe power collapse"); } else { ipa_eth_dev_log(eth_dev, "Disabled MSM PCIe power collapse"); } return rc; } struct ipa_eth_bus ipa_eth_pci_bus = { .bus = &pci_bus_type, .register_net_driver = ipa_eth_pci_register_net_driver, .unregister_net_driver = ipa_eth_pci_unregister_net_driver, .enable_pc = ipa_eth_pci_enable_pc, .disable_pc = ipa_eth_pci_disable_pc, }; int ipa_eth_pci_modinit(struct dentry *dbgfs_root) Loading drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_pm.c +29 −1 Original line number Diff line number Diff line Loading @@ -10,9 +10,12 @@ * GNU General Public License for more details. */ #include <linux/ethtool.h> #include "ipa_eth_i.h" #define IPA_ETH_MIN_BW_MBPS 1 #define IPA_ETH_MAX_BW_MBPS 100000 static void ipa_eth_pm_callback(void *arg, enum ipa_pm_cb_event event) { Loading Loading @@ -92,10 +95,35 @@ int ipa_eth_pm_deactivate(struct ipa_eth_device *eth_dev) return ipa_pm_deactivate_sync(eth_dev->pm_handle); } static u32 __fetch_ethtool_link_speed(struct ipa_eth_device *eth_dev) { int rc; struct ethtool_link_ksettings link_ksettings; rc = __ethtool_get_link_ksettings(eth_dev->net_dev, &link_ksettings); if (rc) { ipa_eth_dev_err(eth_dev, "Failed to obtain link settings via ethtool"); return 0; } return link_ksettings.base.speed; } int ipa_eth_pm_vote_bw(struct ipa_eth_device *eth_dev) { u32 link_speed; int throughput; if (!ipa_pm_is_used()) return 0; return ipa_pm_set_throughput(eth_dev->pm_handle, IPA_ETH_MIN_BW_MBPS); link_speed = __fetch_ethtool_link_speed(eth_dev); throughput = clamp_val(link_speed, IPA_ETH_MIN_BW_MBPS, IPA_ETH_MAX_BW_MBPS); ipa_eth_dev_log(eth_dev, "Link speed is %u, setting throughput as %d", link_speed, throughput); return ipa_pm_set_throughput(eth_dev->pm_handle, throughput); } Loading
drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth.c +18 −1 Original line number Diff line number Diff line Loading @@ -170,6 +170,14 @@ static int ipa_eth_start_device(struct ipa_eth_device *eth_dev) return rc; } rc = ipa_eth_bus_disable_pc(eth_dev); if (rc) { ipa_eth_dev_err(eth_dev, "Failed to disable bus power collapse"); eth_dev->of_state = IPA_ETH_OF_ST_ERROR; return rc; } ipa_eth_dev_log(eth_dev, "Started device"); eth_dev->of_state = IPA_ETH_OF_ST_STARTED; Loading @@ -187,6 +195,14 @@ static int ipa_eth_stop_device(struct ipa_eth_device *eth_dev) if (eth_dev->of_state != IPA_ETH_OF_ST_STARTED) return -EFAULT; rc = ipa_eth_bus_enable_pc(eth_dev); if (rc) { ipa_eth_dev_err(eth_dev, "Failed to enable bus power collapse"); eth_dev->of_state = IPA_ETH_OF_ST_ERROR; return rc; } rc = ipa_eth_offload_stop(eth_dev); if (rc) { ipa_eth_dev_err(eth_dev, "Failed to stop offload"); Loading Loading @@ -987,6 +1003,7 @@ static void ipa_eth_ipc_log_cleanup(void) int ipa_eth_init(void) { int rc; unsigned int wq_flags = WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE; rc = ipa_eth_ipc_log_init(); if (rc) { Loading @@ -1002,7 +1019,7 @@ int ipa_eth_init(void) goto err_dbgfs; } ipa_eth_wq = alloc_workqueue("ipa_eth", WQ_UNBOUND, 0); ipa_eth_wq = alloc_workqueue("ipa_eth", wq_flags, 0); if (!ipa_eth_wq) { ipa_eth_err("Failed to alloc workqueue"); goto err_wq; Loading
drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_bus.c +37 −0 Original line number Diff line number Diff line Loading @@ -87,9 +87,46 @@ void ipa_eth_bus_unregister_driver(struct ipa_eth_net_driver *nd) { struct ipa_eth_bus *eth_bus = lookup_eth_bus(nd->bus); if (!eth_bus) { ipa_eth_bug("Failed to lookup eth_bus for %s", nd->bus->name); return; } eth_bus->unregister_net_driver(nd); } int ipa_eth_bus_enable_pc(struct ipa_eth_device *eth_dev) { struct ipa_eth_net_driver *nd = eth_dev->nd; struct ipa_eth_bus *eth_bus = lookup_eth_bus(nd->bus); if (!eth_bus) { ipa_eth_dev_bug(eth_dev, "Failed to lookup eth_bus"); return -EFAULT; } if (eth_bus->enable_pc) return eth_bus->enable_pc(eth_dev); return -EFAULT; } int ipa_eth_bus_disable_pc(struct ipa_eth_device *eth_dev) { struct ipa_eth_net_driver *nd = eth_dev->nd; struct ipa_eth_bus *eth_bus = lookup_eth_bus(nd->bus); if (!eth_bus) { ipa_eth_dev_bug(eth_dev, "Failed to lookup eth_bus"); return -EFAULT; } if (eth_bus->disable_pc) return eth_bus->disable_pc(eth_dev); return -EFAULT; } int ipa_eth_bus_modinit(struct dentry *dbgfs_root) { int rc; Loading
drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_i.h +6 −0 Original line number Diff line number Diff line Loading @@ -90,6 +90,9 @@ struct ipa_eth_bus { int (*register_net_driver)(struct ipa_eth_net_driver *nd); void (*unregister_net_driver)(struct ipa_eth_net_driver *nd); int (*enable_pc)(struct ipa_eth_device *eth_dev); int (*disable_pc)(struct ipa_eth_device *eth_dev); }; extern struct ipa_eth_bus ipa_eth_pci_bus; Loading @@ -112,6 +115,9 @@ void ipa_eth_bus_modexit(void); int ipa_eth_bus_register_driver(struct ipa_eth_net_driver *nd); void ipa_eth_bus_unregister_driver(struct ipa_eth_net_driver *nd); int ipa_eth_bus_enable_pc(struct ipa_eth_device *eth_dev); int ipa_eth_bus_disable_pc(struct ipa_eth_device *eth_dev); int ipa_eth_offload_modinit(struct dentry *dbgfs_root); void ipa_eth_offload_modexit(void); Loading
drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_pci.c +168 −7 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #include <linux/debugfs.h> #include <linux/pci.h> #include <linux/msm_pcie.h> #include "ipa_eth_i.h" Loading @@ -25,6 +26,8 @@ struct ipa_eth_pci_driver { int (*probe_real)(struct pci_dev *dev, const struct pci_device_id *id); void (*remove_real)(struct pci_dev *dev); const struct dev_pm_ops *pm_ops_real; }; static LIST_HEAD(pci_drivers); Loading Loading @@ -127,7 +130,7 @@ static int ipa_eth_pci_probe_handler(struct pci_dev *pdev, rc = epci_drv->probe_real(pdev, id); if (rc) { ipa_eth_dev_err(eth_dev, "Failed real PCI probe of devfn=%u"); ipa_eth_err("Failed real PCI probe of devfn=%u"); goto err_probe; } Loading Loading @@ -172,6 +175,10 @@ static void ipa_eth_pci_remove_handler(struct pci_dev *pdev) pdev->driver->name, pdev->devfn); eth_dev = lookup_eth_dev(pdev); if (!eth_dev) { ipa_eth_bug("Failed to lookup pci_dev -> eth_dev"); return; } mutex_lock(&pci_devices_mutex); list_del(ð_dev->bus_device_list); Loading @@ -186,19 +193,113 @@ static void ipa_eth_pci_remove_handler(struct pci_dev *pdev) devm_kfree(dev, eth_dev); } static int ipa_eth_pci_suspend_handler(struct device *dev) { int rc = 0; struct ipa_eth_device *eth_dev; const struct dev_pm_ops *pm_ops_real; struct pci_dev *pci_dev = to_pci_dev(dev); eth_dev = lookup_eth_dev(pci_dev); if (!eth_dev) { ipa_eth_bug("Failed to lookup pci_dev -> eth_dev"); return -EFAULT; } pm_ops_real = ((struct ipa_eth_pci_driver *)eth_dev->bus_priv)->pm_ops_real; /* When offload is started, PCI power collapse is already disabled by * the ipa_eth_pci_disable_pc() api. Nonetheless, we still need to do * a dummy PCI config space save so that the PCIe framework will not by * itself perform a config space save-restore. */ if (eth_dev->of_state == IPA_ETH_OF_ST_STARTED) { ipa_eth_dev_log(eth_dev, "Device suspend performing dummy config space save"); rc = pci_save_state(pci_dev); } else { ipa_eth_dev_log(eth_dev, "Device suspend delegated to net driver"); rc = pm_ops_real->suspend(dev); } if (rc) ipa_eth_dev_log(eth_dev, "Device suspend failed"); else ipa_eth_dev_log(eth_dev, "Device suspend complete"); return rc; } static int ipa_eth_pci_resume_handler(struct device *dev) { int rc = 0; struct ipa_eth_device *eth_dev; const struct dev_pm_ops *pm_ops_real; struct pci_dev *pci_dev = to_pci_dev(dev); eth_dev = lookup_eth_dev(pci_dev); if (!eth_dev) { ipa_eth_bug("Failed to lookup pci_dev -> eth_dev"); return -EFAULT; } pm_ops_real = ((struct ipa_eth_pci_driver *)eth_dev->bus_priv)->pm_ops_real; /* During suspend, RC power collapse would not have happened if offload * was started. Ignore resume callback since the device does not need * to be re-initialized. */ if (eth_dev->of_state == IPA_ETH_OF_ST_STARTED) { ipa_eth_dev_log(eth_dev, "Device resume performing nop"); rc = 0; } else { ipa_eth_dev_log(eth_dev, "Device resume delegated to net driver"); rc = pm_ops_real->resume(dev); } if (rc) ipa_eth_dev_log(eth_dev, "Device resume failed"); else ipa_eth_dev_log(eth_dev, "Device resume complete"); return 0; } /* MSM PCIe driver invokes only suspend and resume callbacks, other operations * can be ignored. */ static const struct dev_pm_ops ipa_eth_pci_pm_ops = { .suspend = ipa_eth_pci_suspend_handler, .resume = ipa_eth_pci_resume_handler, }; static int ipa_eth_pci_register_net_driver(struct ipa_eth_net_driver *nd) { struct ipa_eth_pci_driver *epci_drv = NULL; struct pci_driver *pci_drv = container_of(nd->driver, struct pci_driver, driver); struct pci_driver *pci_drv; struct ipa_eth_pci_driver *epci_drv; if (!nd) { ipa_eth_err("Network driver is NULL"); return -EINVAL; } pci_drv = to_pci_driver(nd->driver); if (WARN_ON(!pci_drv->probe || !pci_drv->remove)) { ipa_eth_err("PCI driver lacking probe/remove callbacks"); ipa_eth_err("PCI net driver %s lacking probe/remove callbacks", nd->name); return -EFAULT; } if (!pci_drv->driver.pm || !pci_drv->driver.pm->suspend || !pci_drv->driver.pm->resume) { ipa_eth_err("PCI net driver %s does not support PM ops", nd->name); return -EFAULT; } Loading @@ -212,6 +313,9 @@ static int ipa_eth_pci_register_net_driver(struct ipa_eth_net_driver *nd) epci_drv->remove_real = pci_drv->remove; pci_drv->remove = ipa_eth_pci_remove_handler; epci_drv->pm_ops_real = pci_drv->driver.pm; pci_drv->driver.pm = &ipa_eth_pci_pm_ops; epci_drv->nd = nd; mutex_lock(&pci_drivers_mutex); Loading @@ -224,8 +328,7 @@ static int ipa_eth_pci_register_net_driver(struct ipa_eth_net_driver *nd) static void ipa_eth_pci_unregister_net_driver(struct ipa_eth_net_driver *nd) { struct pci_driver *pci_drv = container_of(nd->driver, struct pci_driver, driver); struct pci_driver *pci_drv = to_pci_driver(nd->driver); struct ipa_eth_pci_driver *epci_drv = lookup_epci_driver(pci_drv); mutex_lock(&pci_drivers_mutex); Loading @@ -235,14 +338,72 @@ static void ipa_eth_pci_unregister_net_driver(struct ipa_eth_net_driver *nd) pci_drv->probe = epci_drv->probe_real; pci_drv->remove = epci_drv->remove_real; pci_drv->driver.pm = epci_drv->pm_ops_real; memset(epci_drv, 0, sizeof(*epci_drv)); kfree(epci_drv); } /** * ipa_eth_pci_enable_pc() - Permit power collapse of the PCI root port * @eth_dev: Device attached to the PCI bus * * This function instructs the MSM PCIe bus driver to permit power collapse * of the root complex when Linux goes to suspend state. * * Return: 0 on success, non-zero otherwise */ static int ipa_eth_pci_enable_pc(struct ipa_eth_device *eth_dev) { int rc; struct pci_dev *pci_dev = to_pci_dev(eth_dev->dev); rc = msm_pcie_pm_control(MSM_PCIE_ENABLE_PC, pci_dev->bus->number, pci_dev, NULL, MSM_PCIE_CONFIG_INVALID); if (rc) { ipa_eth_dev_err(eth_dev, "Failed to enable MSM PCIe power collapse"); } else { ipa_eth_dev_log(eth_dev, "Enabled MSM PCIe power collapse"); } return rc; } /** * ipa_eth_pci_disable_pc() - Prevent power collapse of the PCI root port * @eth_dev: Device attached to the PCI bus * * This function instructs the MSM PCIe bus driver to prevent power collapse * of the root complex and connected EPs when Linux goes to suspend state. * * Return: 0 on success, non-zero otherwise */ static int ipa_eth_pci_disable_pc(struct ipa_eth_device *eth_dev) { int rc; struct pci_dev *pci_dev = to_pci_dev(eth_dev->dev); rc = msm_pcie_pm_control(MSM_PCIE_DISABLE_PC, pci_dev->bus->number, pci_dev, NULL, MSM_PCIE_CONFIG_INVALID); if (rc) { ipa_eth_dev_err(eth_dev, "Failed to disable MSM PCIe power collapse"); } else { ipa_eth_dev_log(eth_dev, "Disabled MSM PCIe power collapse"); } return rc; } struct ipa_eth_bus ipa_eth_pci_bus = { .bus = &pci_bus_type, .register_net_driver = ipa_eth_pci_register_net_driver, .unregister_net_driver = ipa_eth_pci_unregister_net_driver, .enable_pc = ipa_eth_pci_enable_pc, .disable_pc = ipa_eth_pci_disable_pc, }; int ipa_eth_pci_modinit(struct dentry *dbgfs_root) Loading
drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_pm.c +29 −1 Original line number Diff line number Diff line Loading @@ -10,9 +10,12 @@ * GNU General Public License for more details. */ #include <linux/ethtool.h> #include "ipa_eth_i.h" #define IPA_ETH_MIN_BW_MBPS 1 #define IPA_ETH_MAX_BW_MBPS 100000 static void ipa_eth_pm_callback(void *arg, enum ipa_pm_cb_event event) { Loading Loading @@ -92,10 +95,35 @@ int ipa_eth_pm_deactivate(struct ipa_eth_device *eth_dev) return ipa_pm_deactivate_sync(eth_dev->pm_handle); } static u32 __fetch_ethtool_link_speed(struct ipa_eth_device *eth_dev) { int rc; struct ethtool_link_ksettings link_ksettings; rc = __ethtool_get_link_ksettings(eth_dev->net_dev, &link_ksettings); if (rc) { ipa_eth_dev_err(eth_dev, "Failed to obtain link settings via ethtool"); return 0; } return link_ksettings.base.speed; } int ipa_eth_pm_vote_bw(struct ipa_eth_device *eth_dev) { u32 link_speed; int throughput; if (!ipa_pm_is_used()) return 0; return ipa_pm_set_throughput(eth_dev->pm_handle, IPA_ETH_MIN_BW_MBPS); link_speed = __fetch_ethtool_link_speed(eth_dev); throughput = clamp_val(link_speed, IPA_ETH_MIN_BW_MBPS, IPA_ETH_MAX_BW_MBPS); ipa_eth_dev_log(eth_dev, "Link speed is %u, setting throughput as %d", link_speed, throughput); return ipa_pm_set_throughput(eth_dev->pm_handle, throughput); }