From f628a32c05703962e85d86e2585da899a2c41cdb Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Mon, 13 Jan 2020 15:13:43 +0800 Subject: [PATCH] cnss2: Set WLAON_QFPROM_PWR_CTRL_REG during power on and off After device power on and link config, clear VDD4BLOW_EN bit of WLAON_QFPROM_PWR_CTRL_REG register to disable OTP write. Before device link suspend and power off, clear VDD4BLOW_EN bit and set SHUTDOWN_EN bit. Also add a 1ms sleep after writing WLAON_QFPROM_PWR_CTRL_REG register. Add a DTSI entry to control above operations. Change-Id: Icb307a85bb5d283b78bdd7615f9a4757867c8075 CRs-Fixed: 2601850 Signed-off-by: Yue Ma --- drivers/net/wireless/cnss2/main.c | 14 +++++- drivers/net/wireless/cnss2/main.h | 3 +- drivers/net/wireless/cnss2/pci.c | 71 ++++++++++++++++++++++++++++++- drivers/net/wireless/cnss2/reg.h | 4 +- 4 files changed, 87 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 43178fa01c48..0fbf8b7e7aae 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */ #include #include @@ -2070,6 +2070,17 @@ static void cnss_init_control_params(struct cnss_plat_data *plat_priv) plat_priv->ctrl_params.time_sync_period = CNSS_TIME_SYNC_PERIOD_DEFAULT; } +static void cnss_get_wlaon_pwr_ctrl_info(struct cnss_plat_data *plat_priv) +{ + struct device *dev = &plat_priv->plat_dev->dev; + + plat_priv->set_wlaon_pwr_ctrl = + of_property_read_bool(dev->of_node, "qcom,set-wlaon-pwr-ctrl"); + + cnss_pr_dbg("set_wlaon_pwr_ctrl is %d\n", + plat_priv->set_wlaon_pwr_ctrl); +} + static const struct platform_device_id cnss_platform_id_table[] = { { .name = "qca6174", .driver_data = QCA6174_DEVICE_ID, }, { .name = "qca6290", .driver_data = QCA6290_DEVICE_ID, }, @@ -2140,6 +2151,7 @@ static int cnss_probe(struct platform_device *plat_dev) INIT_LIST_HEAD(&plat_priv->vreg_list); INIT_LIST_HEAD(&plat_priv->clk_list); + cnss_get_wlaon_pwr_ctrl_info(plat_priv); cnss_get_cpr_info(plat_priv); cnss_init_control_params(plat_priv); diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index bb2b575f7f2f..3a88819b8480 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */ #ifndef _CNSS_MAIN_H #define _CNSS_MAIN_H @@ -373,6 +373,7 @@ struct cnss_plat_data { void *get_info_cb_ctx; int (*get_info_cb)(void *ctx, void *event, int event_len); u8 use_nv_mac; + u8 set_wlaon_pwr_ctrl; }; #ifdef CONFIG_ARCH_QCOM diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 5054a0dd78f6..611861ec5afc 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -62,6 +62,9 @@ static DEFINE_SPINLOCK(time_sync_lock); #define MHI_TIMEOUT_OVERWRITE_MS (plat_priv->ctrl_params.mhi_timeout) #define MHI_M2_TIMEOUT_MS (plat_priv->ctrl_params.mhi_m2_timeout) +#define WLAON_PWR_CTRL_SHUTDOWN_DELAY_MIN_US 1000 +#define WLAON_PWR_CTRL_SHUTDOWN_DELAY_MAX_US 2000 + #define FORCE_WAKE_DELAY_MIN_US 4000 #define FORCE_WAKE_DELAY_MAX_US 6000 #define FORCE_WAKE_DELAY_TIMEOUT_US 60000 @@ -480,6 +483,7 @@ static int cnss_pci_force_wake_get(struct cnss_pci_data *pci_priv) if (cnss_pci_is_device_awake(dev) != true) { cnss_pr_err("Timed out to request force wake\n"); + cnss_pci_force_wake_release(dev); return -ETIMEDOUT; } @@ -1131,6 +1135,62 @@ static void cnss_pci_deinit_mhi(struct cnss_pci_data *pci_priv) cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT); } +static void cnss_pci_set_wlaon_pwr_ctrl(struct cnss_pci_data *pci_priv, + bool set_vddd4blow, bool set_shutdown, + bool do_force_wake) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + int ret; + u32 val; + + if (!plat_priv->set_wlaon_pwr_ctrl) + return; + + if (do_force_wake) + if (cnss_pci_force_wake_get(pci_priv)) + return; + + ret = cnss_pci_reg_read(pci_priv, QCA6390_WLAON_QFPROM_PWR_CTRL_REG, + &val); + if (ret) { + cnss_pr_err("Failed to read register offset 0x%x, err = %d\n", + QCA6390_WLAON_QFPROM_PWR_CTRL_REG, ret); + goto force_wake_put; + } + + cnss_pr_dbg("Read register offset 0x%x, val = 0x%x\n", + QCA6390_WLAON_QFPROM_PWR_CTRL_REG, val); + + if (set_vddd4blow) + val |= QFPROM_PWR_CTRL_VDD4BLOW_SW_EN_MASK; + else + val &= ~QFPROM_PWR_CTRL_VDD4BLOW_SW_EN_MASK; + + if (set_shutdown) + val |= QFPROM_PWR_CTRL_SHUTDOWN_EN_MASK; + else + val &= ~QFPROM_PWR_CTRL_SHUTDOWN_EN_MASK; + + ret = cnss_pci_reg_write(pci_priv, QCA6390_WLAON_QFPROM_PWR_CTRL_REG, + val); + if (ret) { + cnss_pr_err("Failed to write register offset 0x%x, err = %d\n", + QCA6390_WLAON_QFPROM_PWR_CTRL_REG, ret); + goto force_wake_put; + } + + cnss_pr_dbg("Write val 0x%x to register offset 0x%x\n", val, + QCA6390_WLAON_QFPROM_PWR_CTRL_REG); + + if (set_shutdown) + usleep_range(WLAON_PWR_CTRL_SHUTDOWN_DELAY_MIN_US, + WLAON_PWR_CTRL_SHUTDOWN_DELAY_MAX_US); + +force_wake_put: + if (do_force_wake) + cnss_pci_force_wake_put(pci_priv); +} + static int cnss_pci_get_device_timestamp(struct cnss_pci_data *pci_priv, u64 *time_us) { @@ -1671,6 +1731,7 @@ retry: goto power_off; } + cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, false, false); timeout = cnss_get_boot_timeout(&pci_priv->pci_dev->dev); ret = cnss_pci_start_mhi(pci_priv); @@ -1705,6 +1766,7 @@ retry: return 0; stop_mhi: + cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, true, true); cnss_pci_power_off_mhi(pci_priv); cnss_suspend_pci_link(pci_priv); cnss_pci_deinit_mhi(pci_priv); @@ -1718,6 +1780,7 @@ static int cnss_qca6290_shutdown(struct cnss_pci_data *pci_priv) { int ret = 0; struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + int do_force_wake = true; cnss_pci_pm_runtime_resume(pci_priv); @@ -1744,6 +1807,10 @@ static int cnss_qca6290_shutdown(struct cnss_pci_data *pci_priv) goto skip_power_off; } + if (test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) + do_force_wake = false; + + cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, true, do_force_wake); cnss_pci_power_off_mhi(pci_priv); ret = cnss_suspend_pci_link(pci_priv); if (ret) @@ -4113,6 +4180,7 @@ static int cnss_pci_probe(struct pci_dev *pci_dev, case QCA6290_DEVICE_ID: case QCA6390_DEVICE_ID: case QCA6490_DEVICE_ID: + cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, false, false); timer_setup(&pci_priv->dev_rddm_timer, cnss_dev_rddm_timeout_hdlr, 0); INIT_DELAYED_WORK(&pci_priv->time_sync_work, @@ -4127,11 +4195,10 @@ static int cnss_pci_probe(struct pci_dev *pci_dev, goto disable_bus; } cnss_pci_get_link_status(pci_priv); - cnss_pci_config_regs(pci_priv); - if (EMULATION_HW) break; + cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, true, false); ret = cnss_suspend_pci_link(pci_priv); if (ret) cnss_pr_err("Failed to suspend PCI link, err = %d\n", diff --git a/drivers/net/wireless/cnss2/reg.h b/drivers/net/wireless/cnss2/reg.h index 4052de4b78c5..6e7b70960625 100644 --- a/drivers/net/wireless/cnss2/reg.h +++ b/drivers/net/wireless/cnss2/reg.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2019, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ #ifndef _CNSS_REG_H #define _CNSS_REG_H @@ -204,6 +204,8 @@ #define QCA6390_WLAON_WL_CLK_CNTL_KDF_REG 0x1F80314 #define QCA6390_WLAON_WL_CLK_CNTL_PMU_HFRC_REG 0x1F80318 #define QCA6390_WLAON_QFPROM_PWR_CTRL_REG 0x1F8031C +#define QFPROM_PWR_CTRL_VDD4BLOW_SW_EN_MASK 0x4 +#define QFPROM_PWR_CTRL_SHUTDOWN_EN_MASK 0x1 #define QCA6390_WLAON_DLY_CONFIG 0x1F80400 #define QCA6390_WLAON_WLAON_Q6_IRQ_REG 0x1F80404 #define QCA6390_WLAON_PCIE_INTF_SW_CFG_REG 0x1F80408