From 639f0df4a091985f0f0b8bf3dc188903f81ab987 Mon Sep 17 00:00:00 2001 From: Komal Seelam Date: Wed, 30 Mar 2016 11:20:57 +0530 Subject: [PATCH 1/5] cnss: Control vdd-wlan-xtal-aon-supply regulator in cnss platform driver vdd-wlan-xtal-aon-supply(LD0-4) need to be voted by cnss platform driver to keep wlan XO(LDO-5) ON during APPS suspend on mdmcalifornium platforms when wlan is turned ON as per Power UP sequence. Currently LDO-4 is only voted by USB driver when USB is connected. On removing USB, LDO-4 is turned off resulting LDO-5 turned off. When WLAN is turned on removing LDO-5 vote resulted in beaconing failures on mdmcalifornium platforms. Hence add a dummy vote to LDO-4 when WLAN is turned ON. Change-Id: I0c6e7a6c2dc6a51fb19a9d31ceb6823fe9a05ae2 CRs-Fixed: 996537 Signed-off-by: Komal Seelam --- .../devicetree/bindings/cnss/cnss-wlan.txt | 3 + drivers/net/wireless/cnss/cnss_pci.c | 138 ++++++++++++++---- 2 files changed, 115 insertions(+), 26 deletions(-) diff --git a/Documentation/devicetree/bindings/cnss/cnss-wlan.txt b/Documentation/devicetree/bindings/cnss/cnss-wlan.txt index 09626ae3a72c..101be70d33b2 100644 --- a/Documentation/devicetree/bindings/cnss/cnss-wlan.txt +++ b/Documentation/devicetree/bindings/cnss/cnss-wlan.txt @@ -29,6 +29,9 @@ Optional properties: has exclusive access to UART. - vdd-wlan-io-supply: phandle to the 1.8V IO regulator for QCA6174 - vdd-wlan-xtal-supply: phandle to the 1.8V XTAL regulator for QCA6174 + - vdd-wlan-xtal-aon-supply: phandle to the LDO-4 regulator. This is needed + on platforms where XTAL regulator depends on + always on regulator in VDDmin. - vdd-wlan-core-supply: phandle to the 1.3V CORE regulator for QCA6174 - vdd-wlan-sp2t-supply: phandle to the 2.7V SP2T regulator for QCA6174 - qcom,wlan-smmu-iova-address: I/O virtual address range as diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c index daa7f65690db..c1a6c2dea8ce 100644 --- a/drivers/net/wireless/cnss/cnss_pci.c +++ b/drivers/net/wireless/cnss/cnss_pci.c @@ -109,6 +109,7 @@ static struct cnss_fw_files FW_FILES_DEFAULT = { #define WLAN_VREG_NAME "vdd-wlan" #define WLAN_VREG_IO_NAME "vdd-wlan-io" #define WLAN_VREG_XTAL_NAME "vdd-wlan-xtal" +#define WLAN_VREG_XTAL_AON_NAME "vdd-wlan-xtal-aon" #define WLAN_VREG_CORE_NAME "vdd-wlan-core" #define WLAN_VREG_SP2T_NAME "vdd-wlan-sp2t" #define WLAN_SWREG_NAME "wlan-soc-swreg" @@ -181,6 +182,7 @@ struct cnss_wlan_vreg_info { struct regulator *ant_switch; struct regulator *wlan_reg_io; struct regulator *wlan_reg_xtal; + struct regulator *wlan_reg_xtal_aon; struct regulator *wlan_reg_core; struct regulator *wlan_reg_sp2t; bool state; @@ -334,6 +336,15 @@ static int cnss_wlan_vreg_on(struct cnss_wlan_vreg_info *vreg_info) } } + if (vreg_info->wlan_reg_xtal_aon) { + ret = regulator_enable(vreg_info->wlan_reg_xtal_aon); + if (ret) { + pr_err("%s: wlan_reg_xtal_aon enable failed\n", + __func__); + goto error_enable_reg_xtal_aon; + } + } + if (vreg_info->wlan_reg_xtal) { ret = regulator_enable(vreg_info->wlan_reg_xtal); if (ret) { @@ -382,6 +393,9 @@ error_enable_reg_sp2t: if (vreg_info->wlan_reg_xtal) regulator_disable(vreg_info->wlan_reg_xtal); error_enable_reg_xtal: + if (vreg_info->wlan_reg_xtal_aon) + regulator_disable(vreg_info->wlan_reg_xtal_aon); +error_enable_reg_xtal_aon: if (vreg_info->wlan_reg_io) regulator_disable(vreg_info->wlan_reg_io); error_enable_reg_io: @@ -433,6 +447,15 @@ static int cnss_wlan_vreg_off(struct cnss_wlan_vreg_info *vreg_info) } } + if (vreg_info->wlan_reg_xtal_aon) { + ret = regulator_disable(vreg_info->wlan_reg_xtal_aon); + if (ret) { + pr_err("%s: wlan_reg_xtal_aon disable failed\n", + __func__); + goto error_disable; + } + } + if (vreg_info->wlan_reg_io) { ret = regulator_disable(vreg_info->wlan_reg_io); if (ret) { @@ -583,6 +606,89 @@ static int cnss_pinctrl_init(struct cnss_wlan_gpio_info *gpio_info, return ret; } +static void cnss_disable_xtal_ldo(struct platform_device *pdev) +{ + struct cnss_wlan_vreg_info *info = &penv->vreg_info; + + if (info->wlan_reg_xtal) { + regulator_disable(info->wlan_reg_xtal); + regulator_put(info->wlan_reg_xtal); + } + + if (info->wlan_reg_xtal_aon) { + regulator_disable(info->wlan_reg_xtal_aon); + regulator_put(info->wlan_reg_xtal_aon); + } +} + +static int cnss_enable_xtal_ldo(struct platform_device *pdev) +{ + int ret = 0; + struct cnss_wlan_vreg_info *info = &penv->vreg_info; + + if (!of_get_property(pdev->dev.of_node, + WLAN_VREG_XTAL_AON_NAME "-supply", NULL)) + goto enable_xtal; + + info->wlan_reg_xtal_aon = regulator_get(&pdev->dev, + WLAN_VREG_XTAL_AON_NAME); + if (IS_ERR(info->wlan_reg_xtal_aon)) { + ret = PTR_ERR(info->wlan_reg_xtal_aon); + pr_err("%s: XTAL AON Regulator get failed err:%d\n", __func__, + ret); + return ret; + } + + ret = regulator_enable(info->wlan_reg_xtal_aon); + if (ret) { + pr_err("%s: VREG_XTAL_ON enable failed\n", __func__); + goto end; + } + +enable_xtal: + + if (!of_get_property(pdev->dev.of_node, + WLAN_VREG_XTAL_NAME "-supply", NULL)) + goto out_disable_xtal_aon; + + info->wlan_reg_xtal = regulator_get(&pdev->dev, WLAN_VREG_XTAL_NAME); + + if (IS_ERR(info->wlan_reg_xtal)) { + ret = PTR_ERR(info->wlan_reg_xtal); + pr_err("%s XTAL Regulator get failed err:%d\n", __func__, ret); + goto out_disable_xtal_aon; + } + + ret = regulator_set_voltage(info->wlan_reg_xtal, WLAN_VREG_XTAL_MIN, + WLAN_VREG_XTAL_MAX); + if (ret) { + pr_err("%s: Set wlan_vreg_xtal failed!\n", __func__); + goto out_put_xtal; + } + + ret = regulator_enable(info->wlan_reg_xtal); + if (ret) { + pr_err("%s: Enable wlan_vreg_xtal failed!\n", __func__); + goto out_put_xtal; + } + + return 0; + +out_put_xtal: + if (info->wlan_reg_xtal) + regulator_put(info->wlan_reg_xtal); + +out_disable_xtal_aon: + if (info->wlan_reg_xtal_aon) + regulator_disable(info->wlan_reg_xtal_aon); + +end: + if (info->wlan_reg_xtal_aon) + regulator_put(info->wlan_reg_xtal_aon); + + return ret; +} + static int cnss_wlan_get_resources(struct platform_device *pdev) { int ret = 0; @@ -660,27 +766,8 @@ static int cnss_wlan_get_resources(struct platform_device *pdev) } } - if (of_get_property(pdev->dev.of_node, - WLAN_VREG_XTAL_NAME"-supply", NULL)) { - vreg_info->wlan_reg_xtal = - regulator_get(&pdev->dev, WLAN_VREG_XTAL_NAME); - if (!IS_ERR(vreg_info->wlan_reg_xtal)) { - ret = regulator_set_voltage(vreg_info->wlan_reg_xtal, - WLAN_VREG_XTAL_MIN, WLAN_VREG_XTAL_MAX); - if (ret) { - pr_err("%s: Set wlan_vreg_xtal failed!\n", - __func__); - goto err_reg_xtal_set; - } - - ret = regulator_enable(vreg_info->wlan_reg_xtal); - if (ret) { - pr_err("%s: Enable wlan_vreg_xtal failed!\n", - __func__); - goto err_reg_xtal_enable; - } - } - } + if (cnss_enable_xtal_ldo(pdev)) + goto err_reg_xtal_enable; if (of_get_property(pdev->dev.of_node, WLAN_VREG_SP2T_NAME"-supply", NULL)) { @@ -850,13 +937,10 @@ err_reg_sp2t_enable: err_reg_sp2t_set: if (vreg_info->wlan_reg_sp2t) regulator_put(vreg_info->wlan_reg_sp2t); - if (vreg_info->wlan_reg_xtal) - regulator_disable(vreg_info->wlan_reg_xtal); + + cnss_disable_xtal_ldo(pdev); err_reg_xtal_enable: -err_reg_xtal_set: - if (vreg_info->wlan_reg_xtal) - regulator_put(vreg_info->wlan_reg_xtal); if (vreg_info->wlan_reg_io) regulator_disable(vreg_info->wlan_reg_io); @@ -901,6 +985,8 @@ static void cnss_wlan_release_resources(void) regulator_put(vreg_info->wlan_reg_sp2t); if (vreg_info->wlan_reg_xtal) regulator_put(vreg_info->wlan_reg_xtal); + if (vreg_info->wlan_reg_xtal_aon) + regulator_put(vreg_info->wlan_reg_xtal_aon); if (vreg_info->wlan_reg_io) regulator_put(vreg_info->wlan_reg_io); regulator_put(vreg_info->wlan_reg); From ae275bab72c704ed0a7545f13ea5f8a33e379aad Mon Sep 17 00:00:00 2001 From: Komal Seelam Date: Wed, 30 Mar 2016 15:57:06 +0530 Subject: [PATCH 2/5] cnss: Add optional property to control wlan_en gpio for Dual-Wifi platforms The WIFI PCIe Clock is controlled by WLAN_EN gpio. On Dual-WiFi Platforms, QCA WIFI SDIO chip clock sourced by PCIe Clock. Turning off WLAN_EN gpio would result in SDIO WIFI Functional Failures. Control the gpio, based on the platform. CRs-Fixed: 990796 Change-Id: I1399cd8aa53d1c6fd6b8ff0c5cce66b978770dbd Signed-off-by: Komal Seelam Signed-off-by: Sarada Prasanna Garnayak --- .../devicetree/bindings/cnss/cnss-wlan.txt | 2 ++ drivers/net/wireless/cnss/cnss_pci.c | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/Documentation/devicetree/bindings/cnss/cnss-wlan.txt b/Documentation/devicetree/bindings/cnss/cnss-wlan.txt index 101be70d33b2..6d63d1123f4c 100644 --- a/Documentation/devicetree/bindings/cnss/cnss-wlan.txt +++ b/Documentation/devicetree/bindings/cnss/cnss-wlan.txt @@ -42,6 +42,8 @@ Optional properties: - reg-names: Names of the memory regions defined in reg entry - wlan-bootstrap-gpio: WLAN_BOOTSTRAP GPIO signal specified by QCA6174 which should be drived depending on platforms + - qcom,is-dual-wifi-enabled: Boolean property to control wlan enable(wlan-en) + gpio on dual-wifi platforms. Example: diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c index c1a6c2dea8ce..efe0e7ad43d0 100644 --- a/drivers/net/wireless/cnss/cnss_pci.c +++ b/drivers/net/wireless/cnss/cnss_pci.c @@ -223,6 +223,10 @@ struct index_file { u8 file_name[13]; }; +struct cnss_dual_wifi { + bool is_dual_wifi_enabled; +}; + /** * struct wlan_mac_addr - Structure to hold WLAN MAC Address * @mac_addr: MAC address @@ -300,6 +304,7 @@ static struct cnss_data { int wlan_bootstrap_gpio; atomic_t auto_suspended; bool monitor_wake_intr; + struct cnss_dual_wifi dual_wifi_info; } *penv; static unsigned int pcie_link_down_panic; @@ -576,6 +581,11 @@ static void cnss_wlan_gpio_set(struct cnss_wlan_gpio_info *info, bool state) return; } + if (state == WLAN_EN_LOW && penv->dual_wifi_info.is_dual_wifi_enabled) { + pr_debug("%s Dual WiFi enabled\n", __func__); + return; + } + gpio_set_value(info->num, state); info->state = state; @@ -2899,6 +2909,9 @@ static int cnss_probe(struct platform_device *pdev) penv->subsys_handle = subsystem_get(penv->subsysdesc.name); + if (of_property_read_bool(dev->of_node, "qcom,is-dual-wifi-enabled")) + penv->dual_wifi_info.is_dual_wifi_enabled = true; + if (of_property_read_u32(dev->of_node, "qcom,wlan-ramdump-dynamic", &ramdump_size) == 0) { penv->ramdump_addr = dma_alloc_coherent(&pdev->dev, From ec79695605b6ba27190d9f74bc9febf2f7042b56 Mon Sep 17 00:00:00 2001 From: Komal Seelam Date: Fri, 6 May 2016 13:06:00 +0530 Subject: [PATCH 3/5] cnss: Disable wlan irq when PCIe link down is detected Interrupt Storm is observed on the APPS side, when PCIe link down is simulated, as FW keeps generating interrupts to host, as it's not aware of the PCIe link down. When PCIe link down is identified, wlan platform driver schedules work to start recovery. During Recovery we cut down power to QCA6174, which stops the interrupt storm. During the window where platform driver schedule work and the work actually gets schedules, interrupt storm happened and the APPS triggered watchdog. Change-Id: Ica3c32007bc9b8acce79c5bb2175790ff27180d0 CRs-Fixed: 967956 Signed-off-by: Komal Seelam --- drivers/net/wireless/cnss/cnss_pci.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c index efe0e7ad43d0..a5a308667003 100644 --- a/drivers/net/wireless/cnss/cnss_pci.c +++ b/drivers/net/wireless/cnss/cnss_pci.c @@ -1960,6 +1960,13 @@ void cnss_schedule_recovery_work(void) } EXPORT_SYMBOL(cnss_schedule_recovery_work); +static inline void __cnss_disable_irq(void *data) +{ + struct pci_dev *pdev = data; + + disable_irq(pdev->irq); +} + void cnss_pci_events_cb(struct msm_pcie_notify *notify) { unsigned long flags; @@ -1983,6 +1990,7 @@ void cnss_pci_events_cb(struct msm_pcie_notify *notify) spin_unlock_irqrestore(&pci_link_down_lock, flags); pr_err("PCI link down, schedule recovery\n"); + __cnss_disable_irq(notify->user); schedule_work(&cnss_pci_recovery_work); break; From 9e5835025b74a8b3d66135fe1a8a2ebf6bf631f5 Mon Sep 17 00:00:00 2001 From: Komal Seelam Date: Tue, 3 May 2016 13:09:05 +0530 Subject: [PATCH 4/5] cnss: Expose Power Up/Down API to WLAN Functional Driver Dynamic Mode Change Feature Request, needs to power ON QCA Chip during system bootup to get Firmware Capabilities. Today the implementation is only during driver load, platform driver power up the chip and call the device driver probe and call device driver remove and cut the chip power during driver unload. As part of this feature request, driver needs API to power down and power up chip after ifconfig down and before ifconfig up. CRs-Fixed: 1009901 Change-Id: I8e964c6bb8d6d8fb6f4bfba17a6b24b97509953c Signed-off-by: Komal Kumar --- drivers/net/wireless/cnss/cnss_common.c | 42 ++++ drivers/net/wireless/cnss/cnss_common.h | 4 + drivers/net/wireless/cnss/cnss_pci.c | 320 ++++++++++++++++++++++++ drivers/net/wireless/cnss/cnss_sdio.c | 10 + include/net/cnss.h | 2 + 5 files changed, 378 insertions(+) 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_ */ From 3136a40d03729a8f7fe33ef869f8c98302b782dc Mon Sep 17 00:00:00 2001 From: Komal Seelam Date: Tue, 10 May 2016 14:36:50 +0530 Subject: [PATCH 5/5] cnss: Provide API to CLD Driver to control SPDT GPIO Antenna Sharing GPIO need to be toggled between QCA PCIe and SDIO devices based on the requirement. If SDIO device is to be operational, this GPIO should be turned ON high. Expose API to CLD Driver to control this GPIO for dual-wifi platforms. CRs-Fixed: 1013494 Change-Id: I271ba273a4c70717f066d219ff02e8f0f56e2ac3 Signed-off-by: Komal Kumar --- drivers/net/wireless/cnss/cnss_sdio.c | 11 +++++++++-- include/net/cnss.h | 3 +++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/cnss/cnss_sdio.c b/drivers/net/wireless/cnss/cnss_sdio.c index 4db745285aef..f773c5993d44 100644 --- a/drivers/net/wireless/cnss/cnss_sdio.c +++ b/drivers/net/wireless/cnss/cnss_sdio.c @@ -49,8 +49,6 @@ #define CNSS_DUMP_NAME "CNSS_WLAN" #define CNSS_PINCTRL_SLEEP_STATE "sleep" #define CNSS_PINCTRL_ACTIVE_STATE "active" -#define PINCTRL_SLEEP 0 -#define PINCTRL_ACTIVE 1 struct cnss_sdio_regulator { struct regulator *wlan_io; @@ -673,6 +671,15 @@ static int cnss_set_pinctrl_state(struct cnss_sdio_data *pdata, bool state) pinctrl_select_state(info->pinctrl, info->sleep); } +int cnss_sdio_configure_spdt(bool state) +{ + if (!cnss_pdata) + return -ENODEV; + + return cnss_set_pinctrl_state(cnss_pdata, state); +} +EXPORT_SYMBOL(cnss_sdio_configure_spdt); + /** * cnss_sdio_wlan_register_driver() - cnss wlan register API * @driver: sdio wlan driver interface from wlan driver. diff --git a/include/net/cnss.h b/include/net/cnss.h index 3206b8d23bae..5508d5e46d18 100644 --- a/include/net/cnss.h +++ b/include/net/cnss.h @@ -19,6 +19,8 @@ #ifdef CONFIG_CNSS #define CNSS_MAX_FILE_NAME 20 +#define PINCTRL_SLEEP 0 +#define PINCTRL_ACTIVE 1 enum cnss_bus_width_type { CNSS_BUS_WIDTH_NONE, @@ -244,4 +246,5 @@ extern int cnss_common_set_wlan_mac_address(struct device *dev, const u8 *in, 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); +extern int cnss_sdio_configure_spdt(bool state); #endif /* _NET_CNSS_H_ */