summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/cnss/cnss_common.c42
-rw-r--r--drivers/net/wireless/cnss/cnss_common.h4
-rw-r--r--drivers/net/wireless/cnss/cnss_pci.c320
-rw-r--r--drivers/net/wireless/cnss/cnss_sdio.c10
-rw-r--r--include/net/cnss.h2
5 files changed, 378 insertions, 0 deletions
diff --git a/drivers/net/wireless/cnss/cnss_common.c b/drivers/net/wireless/cnss/cnss_common.c
index 498dd87e1a91..f63e958b1205 100644
--- a/drivers/net/wireless/cnss/cnss_common.c
+++ b/drivers/net/wireless/cnss/cnss_common.c
@@ -382,3 +382,45 @@ int cnss_common_set_wlan_mac_address(
return ret;
}
EXPORT_SYMBOL(cnss_common_set_wlan_mac_address);
+
+int cnss_power_up(struct device *dev)
+{
+ int ret;
+
+ switch (cnss_get_dev_bus_type(dev)) {
+ case CNSS_BUS_PCI:
+ ret = cnss_pcie_power_up(dev);
+ break;
+ case CNSS_BUS_SDIO:
+ ret = cnss_sdio_power_up(dev);
+ break;
+ default:
+ pr_err("%s: Invalid Bus Type\n", __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(cnss_power_up);
+
+int cnss_power_down(struct device *dev)
+{
+ int ret;
+
+ switch (cnss_get_dev_bus_type(dev)) {
+ case CNSS_BUS_PCI:
+ ret = cnss_pcie_power_down(dev);
+ break;
+ case CNSS_BUS_SDIO:
+ ret = cnss_sdio_power_down(dev);
+ break;
+ default:
+ pr_err("%s: Invalid Bus Type\n", __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(cnss_power_down);
diff --git a/drivers/net/wireless/cnss/cnss_common.h b/drivers/net/wireless/cnss/cnss_common.h
index db5ed02d47ab..0d299f3b6208 100644
--- a/drivers/net/wireless/cnss/cnss_common.h
+++ b/drivers/net/wireless/cnss/cnss_common.h
@@ -37,4 +37,8 @@ int cnss_sdio_set_wlan_mac_address(const u8 *in, uint32_t len);
u8 *cnss_pci_get_wlan_mac_address(uint32_t *num);
u8 *cnss_sdio_get_wlan_mac_address(uint32_t *num);
+int cnss_sdio_power_up(struct device *dev);
+int cnss_sdio_power_down(struct device *dev);
+int cnss_pcie_power_up(struct device *dev);
+int cnss_pcie_power_down(struct device *dev);
#endif /* _NET_CNSS_COMMON_H_ */
diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c
index a5a308667003..801c94859084 100644
--- a/drivers/net/wireless/cnss/cnss_pci.c
+++ b/drivers/net/wireless/cnss/cnss_pci.c
@@ -3471,6 +3471,326 @@ void cnss_runtime_exit(struct device *dev)
pm_runtime_set_active(dev);
}
EXPORT_SYMBOL(cnss_runtime_exit);
+
+static void __cnss_set_pcie_monitor_intr(struct device *dev, bool val)
+{
+ penv->monitor_wake_intr = val;
+}
+
+static void __cnss_set_auto_suspend(struct device *dev, int val)
+{
+ atomic_set(&penv->auto_suspended, val);
+}
+
+static int __cnss_resume_link(struct device *dev, u32 flags)
+{
+ int ret;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u8 bus_num = cnss_get_pci_dev_bus_number(pdev);
+
+ ret = cnss_msm_pcie_pm_control(MSM_PCIE_RESUME, bus_num, pdev, flags);
+ if (ret)
+ pr_err("%s: PCIe link resume failed with flags:%d bus_num:%d\n",
+ __func__, flags, bus_num);
+
+ penv->pcie_link_state = PCIE_LINK_UP;
+
+ return ret;
+}
+
+static int __cnss_suspend_link(struct device *dev, u32 flags)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u8 bus_num = cnss_get_pci_dev_bus_number(pdev);
+ int ret;
+
+ if (!penv->pcie_link_state)
+ return 0;
+
+ ret = cnss_msm_pcie_pm_control(MSM_PCIE_SUSPEND, bus_num, pdev, flags);
+ if (ret) {
+ pr_err("%s: Failed to suspend link\n", __func__);
+ return ret;
+ }
+
+ penv->pcie_link_state = PCIE_LINK_DOWN;
+
+ return ret;
+}
+
+static int __cnss_pcie_recover_config(struct device *dev)
+{
+ int ret;
+
+ ret = cnss_msm_pcie_recover_config(to_pci_dev(dev));
+ if (ret)
+ pr_err("%s: PCIe Recover config failed\n", __func__);
+
+ return ret;
+}
+
+static int __cnss_event_reg(struct device *dev)
+{
+ int ret;
+ struct msm_pcie_register_event *event_reg;
+
+ event_reg = &penv->event_reg;
+
+ event_reg->events = MSM_PCIE_EVENT_LINKDOWN |
+ MSM_PCIE_EVENT_WAKEUP;
+ event_reg->user = to_pci_dev(dev);
+ event_reg->mode = MSM_PCIE_TRIGGER_CALLBACK;
+ event_reg->callback = cnss_pci_events_cb;
+ event_reg->options = MSM_PCIE_CONFIG_NO_RECOVERY;
+
+ ret = cnss_msm_pcie_register_event(event_reg);
+ if (ret)
+ pr_err("%s: PCIe event register failed! %d\n", __func__, ret);
+
+ return ret;
+}
+
+static void __cnss_event_dereg(struct device *dev)
+{
+ cnss_msm_pcie_deregister_event(&penv->event_reg);
+}
+
+static struct pci_dev *__cnss_get_pcie_dev(struct device *dev)
+{
+ int ret;
+ struct pci_dev *pdev = penv->pdev;
+
+ if (pdev)
+ return pdev;
+
+ ret = pci_register_driver(&cnss_wlan_pci_driver);
+ if (ret) {
+ pr_err("%s: pci re-registration failed\n", __func__);
+ return NULL;
+ }
+
+ pdev = penv->pdev;
+
+ return pdev;
+}
+
+static int __cnss_pcie_power_up(struct device *dev)
+{
+ struct cnss_wlan_vreg_info *vreg_info;
+ struct cnss_wlan_gpio_info *gpio_info;
+ int ret;
+
+ vreg_info = &penv->vreg_info;
+ gpio_info = &penv->gpio_info;
+
+ ret = cnss_wlan_vreg_set(vreg_info, VREG_ON);
+ if (ret) {
+ pr_err("%s: WLAN VREG ON Failed\n", __func__);
+ return ret;
+ }
+
+ msleep(POWER_ON_DELAY);
+
+ if (penv->wlan_bootstrap_gpio > 0) {
+ gpio_set_value(penv->wlan_bootstrap_gpio, WLAN_BOOTSTRAP_HIGH);
+ msleep(WLAN_BOOTSTRAP_DELAY);
+ }
+
+ cnss_wlan_gpio_set(gpio_info, WLAN_EN_HIGH);
+ msleep(WLAN_ENABLE_DELAY);
+ return 0;
+}
+
+static int __cnss_pcie_power_down(struct device *dev)
+{
+ struct cnss_wlan_vreg_info *vreg_info;
+ struct cnss_wlan_gpio_info *gpio_info;
+ int ret;
+
+ vreg_info = &penv->vreg_info;
+ gpio_info = &penv->gpio_info;
+
+ cnss_wlan_gpio_set(gpio_info, WLAN_EN_LOW);
+
+ if (penv->wlan_bootstrap_gpio > 0)
+ gpio_set_value(penv->wlan_bootstrap_gpio, WLAN_BOOTSTRAP_LOW);
+
+ ret = cnss_wlan_vreg_set(vreg_info, VREG_OFF);
+ if (ret)
+ pr_err("%s: Failed to turn off 3.3V regulator\n", __func__);
+
+ return ret;
+}
+
+static int __cnss_suspend_link_state(struct device *dev)
+{
+ int ret;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ int link_ind;
+
+ if (!penv->pcie_link_state) {
+ pr_debug("%s: Link is already suspended\n", __func__);
+ return 0;
+ }
+
+ link_ind = penv->pcie_link_down_ind;
+
+ if (!link_ind)
+ pci_save_state(pdev);
+
+ penv->saved_state = link_ind ? NULL : cnss_pci_store_saved_state(pdev);
+
+ ret = link_ind ? __cnss_suspend_link(dev, PM_OPTIONS_SUSPEND_LINK_DOWN)
+ : __cnss_suspend_link(dev, PM_OPTIONS);
+ if (ret) {
+ pr_err("%s: Link Suspend failed in state:%s\n", __func__,
+ link_ind ? "LINK_DOWN" : "LINK_ACTIVE");
+ return ret;
+ }
+
+ penv->pcie_link_state = PCIE_LINK_DOWN;
+
+ return 0;
+}
+
+static int __cnss_restore_pci_config_space(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ int ret = 0;
+
+ if (penv->saved_state)
+ ret = cnss_pci_load_and_free_saved_state(pdev,
+ &penv->saved_state);
+ pci_restore_state(pdev);
+
+ return ret;
+}
+
+static int __cnss_resume_link_state(struct device *dev)
+{
+ int ret;
+ int link_ind;
+
+ if (penv->pcie_link_state) {
+ pr_debug("%s: Link is already in active state\n", __func__);
+ return 0;
+ }
+
+ link_ind = penv->pcie_link_down_ind;
+
+ ret = link_ind ? __cnss_resume_link(dev, PM_OPTIONS_RESUME_LINK_DOWN) :
+ __cnss_resume_link(dev, PM_OPTIONS);
+
+ if (ret) {
+ pr_err("%s: Resume Link failed in link state:%s\n", __func__,
+ link_ind ? "LINK_DOWN" : "LINK_ACTIVE");
+ return ret;
+ }
+
+ penv->pcie_link_state = PCIE_LINK_UP;
+
+ ret = link_ind ? __cnss_pcie_recover_config(dev) :
+ __cnss_restore_pci_config_space(dev);
+
+ if (ret) {
+ pr_err("%s: Link Recovery Config Failed link_state:%s\n",
+ __func__, link_ind ? "LINK_DOWN" : "LINK_ACTIVE");
+ penv->pcie_link_state = PCIE_LINK_DOWN;
+ return ret;
+ }
+
+ penv->pcie_link_down_ind = false;
+ return ret;
+}
+
+int cnss_pcie_power_up(struct device *dev)
+{
+ int ret;
+ struct pci_dev *pdev;
+
+ if (!penv) {
+ pr_err("%s: platform data is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ ret = __cnss_pcie_power_up(dev);
+ if (ret) {
+ pr_err("%s: Power UP Failed\n", __func__);
+ return ret;
+ }
+
+ pdev = __cnss_get_pcie_dev(dev);
+ if (!pdev) {
+ pr_err("%s: PCIe Dev is NULL\n", __func__);
+ goto power_down;
+ }
+
+ ret = __cnss_event_reg(dev);
+
+ if (ret)
+ pr_err("%s: PCIe event registration failed\n", __func__);
+
+ ret = __cnss_resume_link_state(dev);
+
+ if (ret) {
+ pr_err("%s: Link Bring Up Failed\n", __func__);
+ goto event_dereg;
+ }
+
+ __cnss_set_pcie_monitor_intr(dev, true);
+
+ return ret;
+
+event_dereg:
+ __cnss_event_dereg(dev);
+power_down:
+ __cnss_pcie_power_down(dev);
+ pr_err("%s: Device Power Up Failed Fatal Error!\n", __func__);
+ return ret;
+}
+
+static void __cnss_vote_bus_width(struct device *dev, uint32_t option)
+{
+ if (penv->bus_client)
+ msm_bus_scale_client_update_request(penv->bus_client, option);
+}
+
+int cnss_pcie_power_down(struct device *dev)
+{
+ int ret;
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (!penv) {
+ pr_err("%s: Invalid Platform data\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!pdev) {
+ pr_err("%s: Invalid Pdev, Cut Power to device\n", __func__);
+ __cnss_pcie_power_down(dev);
+ return -ENODEV;
+ }
+
+ __cnss_vote_bus_width(dev, CNSS_BUS_WIDTH_NONE);
+ __cnss_event_dereg(dev);
+
+ ret = __cnss_suspend_link_state(dev);
+
+ if (ret) {
+ pr_err("%s: Suspend Link failed\n", __func__);
+ return ret;
+ }
+
+ __cnss_set_pcie_monitor_intr(dev, false);
+ __cnss_set_auto_suspend(dev, 0);
+
+ ret = __cnss_pcie_power_down(dev);
+ if (ret)
+ pr_err("%s: Power Down Failed\n", __func__);
+
+ return ret;
+}
+
module_init(cnss_initialize);
module_exit(cnss_exit);
diff --git a/drivers/net/wireless/cnss/cnss_sdio.c b/drivers/net/wireless/cnss/cnss_sdio.c
index bb64e2149742..4db745285aef 100644
--- a/drivers/net/wireless/cnss/cnss_sdio.c
+++ b/drivers/net/wireless/cnss/cnss_sdio.c
@@ -1168,6 +1168,16 @@ u8 *cnss_sdio_get_wlan_mac_address(uint32_t *num)
return NULL;
}
+int cnss_sdio_power_down(struct device *dev)
+{
+ return 0;
+}
+
+int cnss_sdio_power_up(struct device *dev)
+{
+ return 0;
+}
+
static const struct of_device_id cnss_sdio_dt_match[] = {
{.compatible = "qcom,cnss_sdio"},
{}
diff --git a/include/net/cnss.h b/include/net/cnss.h
index ab9b50100504..3206b8d23bae 100644
--- a/include/net/cnss.h
+++ b/include/net/cnss.h
@@ -242,4 +242,6 @@ extern void cnss_common_schedule_recovery_work(struct device *dev);
extern int cnss_common_set_wlan_mac_address(struct device *dev, const u8 *in,
uint32_t len);
extern u8 *cnss_common_get_wlan_mac_address(struct device *dev, uint32_t *num);
+extern int cnss_power_up(struct device *dev);
+extern int cnss_power_down(struct device *dev);
#endif /* _NET_CNSS_H_ */