icnss2: Support for PMIC GPIO control for WLAN

Some platforms require PMIC GPIO control to trigger the
PON/POFF sequence of the WLAN chip.

Change-Id: I24023694ecb5579bc84cd897a99596fd57fa5eee
CRs-Fixed: 3209939
Signed-off-by: nakul kachhwaha <quic_nkachhwa@quicinc.com>
Signed-off-by: Madhvapathi Sriram <quic_msriram@quicinc.com>
This commit is contained in:
Madhvapathi Sriram 2022-05-31 19:49:11 +05:30
parent 13d743654c
commit b40d1b6b33
6 changed files with 244 additions and 103 deletions

View File

@ -16,6 +16,7 @@ void *icnss_ipc_log_context;
void *icnss_ipc_log_long_context;
void *icnss_ipc_log_smp2p_context;
void *icnss_ipc_soc_wake_context;
void *icnss_ipc_pon_seq_context;
static ssize_t icnss_regwrite_write(struct file *fp,
const char __user *user_buf,
@ -865,6 +866,10 @@ void icnss_debug_init(void)
if (!icnss_ipc_soc_wake_context)
icnss_pr_err("Unable to create log soc_wake context\n");
icnss_ipc_pon_seq_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
"icnss_pon_seq", 0);
if (!icnss_ipc_pon_seq_context)
icnss_pr_err("Unable to create log pon_seq context\n");
}
void icnss_debug_deinit(void)
@ -888,4 +893,9 @@ void icnss_debug_deinit(void)
ipc_log_context_destroy(icnss_ipc_soc_wake_context);
icnss_ipc_soc_wake_context = NULL;
}
if (icnss_ipc_pon_seq_context) {
ipc_log_context_destroy(icnss_ipc_pon_seq_context);
icnss_ipc_pon_seq_context = NULL;
}
}

View File

@ -16,6 +16,7 @@ extern void *icnss_ipc_log_context;
extern void *icnss_ipc_log_long_context;
extern void *icnss_ipc_log_smp2p_context;
extern void *icnss_ipc_soc_wake_context;
extern void *icnss_ipc_pon_seq_context;
#if IS_ENABLED(CONFIG_IPC_LOGGING)
#define icnss_ipc_log_string(_x...) \
@ -29,6 +30,9 @@ extern void *icnss_ipc_soc_wake_context;
#define icnss_ipc_soc_wake_string(_x...) \
ipc_log_string(icnss_ipc_soc_wake_context, _x)
#define icnss_ipc_pon_seq_string(_x...) \
ipc_log_string(icnss_ipc_pon_seq_context, _x)
#else
#define icnss_ipc_log_string(_x...)
@ -37,6 +41,8 @@ extern void *icnss_ipc_soc_wake_context;
#define icnss_ipc_log_smp2p_string(_x...)
#define icnss_ipc_soc_wake_string(_x...)
#define icnss_ipc_pon_seq_string(_x...)
#endif
#define icnss_pr_err(_fmt, ...) do { \
@ -77,6 +83,11 @@ extern void *icnss_ipc_soc_wake_context;
icnss_ipc_soc_wake_string(pr_fmt(_fmt), ##__VA_ARGS__); \
} while (0)
#define icnss_pr_pon_seq(f, ...) do { \
pr_err(f, ##__VA_ARGS__); \
icnss_ipc_pon_seq_string("%s" pr_fmt(f), "", ##__VA_ARGS__); \
} while (0)
#ifdef CONFIG_ICNSS2_DEBUG
#define ICNSS_ASSERT(_condition) do { \
if (!(_condition)) { \

View File

@ -128,7 +128,7 @@ static void icnss_set_plat_priv(struct icnss_priv *priv)
penv = priv;
}
static struct icnss_priv *icnss_get_plat_priv()
struct icnss_priv *icnss_get_plat_priv(void)
{
return penv;
}
@ -1093,8 +1093,20 @@ static int icnss_driver_event_fw_ready_ind(struct icnss_priv *priv, void *data)
icnss_pr_info("WLAN FW is ready: 0x%lx\n", priv->state);
if (!priv->pon_gpio_control)
if (!priv->pon_gpio_control) {
icnss_hw_power_off(priv);
} else {
/* 1. During normal boot when cold cal is not enabled,
* fw expects the power to be on at the chip.
* 2. During recovery, fw expects the power to be
* on at the chip.
* So, turn off only when cold cal is enabled and not in SSR
*/
if (!test_bit(ICNSS_PD_RESTART, &priv->state) &&
priv->cal_done) {
icnss_hw_power_off(priv);
}
}
if (!priv->pdev) {
icnss_pr_err("Device is not ready\n");
@ -4303,6 +4315,7 @@ static int icnss_probe(struct platform_device *pdev)
"qcom,pon-gpio-control")) {
priv->pon_gpio_control = true;
}
spin_lock_init(&priv->event_lock);
spin_lock_init(&priv->on_off_lock);
spin_lock_init(&priv->soc_wake_msg_lock);
@ -4359,7 +4372,6 @@ static int icnss_probe(struct platform_device *pdev)
init_completion(&priv->smp2p_soc_wake_wait);
icnss_runtime_pm_init(priv);
icnss_aop_mbox_init(priv);
if (!priv->pon_gpio_control)
set_bit(ICNSS_COLD_BOOT_CAL, &priv->state);
priv->bdf_download_support = true;
register_trace_android_vh_rproc_recovery_set(rproc_restart_level_notifier, NULL);
@ -4380,7 +4392,7 @@ static int icnss_probe(struct platform_device *pdev)
if (priv->pon_gpio_control) {
ret = icnss_get_pinctrl(priv);
if (ret < 0) {
icnss_pr_err("Fail to get pmic pinctrl config from dt\n");
icnss_pr_err("Fail to get pinctrl config from dt\n");
goto out_unregister_fw_service;
}
}

View File

@ -496,6 +496,9 @@ struct icnss_priv {
bool wpss_supported;
struct icnss_pinctrl_info pinctrl_info;
bool pon_gpio_control;
u32 pon_pinctrl_owners;
u32 pof_pinctrl_owners;
bool pon_in_progress;
};
struct icnss_reg_info {
@ -504,12 +507,6 @@ struct icnss_reg_info {
uint32_t data_len;
};
enum pmic_pwr_seq {
PMIC_PWR_OFF,
PMIC_PWR_ON,
PMIC_PWR_OFF_ON,
};
void icnss_free_qdss_mem(struct icnss_priv *priv);
char *icnss_driver_event_to_str(enum icnss_driver_event_type type);
int icnss_call_driver_uevent(struct icnss_priv *priv,
@ -529,7 +526,7 @@ int icnss_update_cpr_info(struct icnss_priv *priv);
void icnss_add_fw_prefix_name(struct icnss_priv *priv, char *prefix_name,
char *name);
int icnss_aop_mbox_init(struct icnss_priv *priv);
struct icnss_priv *icnss_get_plat_priv(void);
int icnss_get_pinctrl(struct icnss_priv *priv);
int icnss_pmic_gpio_store(struct icnss_priv *priv, uint32_t gpio, bool state);
#endif

View File

@ -622,84 +622,6 @@ static int icnss_clk_off(struct list_head *clk_list)
return 0;
}
static int icnss_pmic_trigger_pon_poff(struct icnss_priv *priv,
enum pmic_pwr_seq seq)
{
int ret = 0;
struct icnss_pinctrl_info *pinctrl_info;
if (!priv) {
icnss_pr_err("plat_priv is NULL!\n");
ret = -ENODEV;
goto out;
}
pinctrl_info = &priv->pinctrl_info;
icnss_pr_err("icnss: PMIC pwr seq %u\n", seq);
switch (seq) {
case PMIC_PWR_OFF:
if (!IS_ERR_OR_NULL(pinctrl_info->wlan_poff_en)) {
ret = pinctrl_select_state(pinctrl_info->pinctrl,
pinctrl_info->wlan_poff_en);
if (ret) {
icnss_pr_err("state for wlan_poff_en err=%d\n",
ret);
goto out;
}
}
mdelay(WLAN_PON_DELAY);
if (!IS_ERR_OR_NULL(pinctrl_info->wlan_poff_dis)) {
ret = pinctrl_select_state(pinctrl_info->pinctrl,
pinctrl_info->wlan_poff_dis);
if (ret) {
icnss_pr_err("state for wlan_poff_dis err=%d\n",
ret);
goto out;
}
}
break;
case PMIC_PWR_ON:
if (!IS_ERR_OR_NULL(pinctrl_info->wlan_pon_en)) {
ret = pinctrl_select_state(pinctrl_info->pinctrl,
pinctrl_info->wlan_pon_en);
if (ret) {
icnss_pr_err("state for wlan_pon_en, err=%d\n",
ret);
goto out;
}
}
mdelay(WLAN_PON_DELAY);
if (!IS_ERR_OR_NULL(pinctrl_info->wlan_pon_dis)) {
ret = pinctrl_select_state(pinctrl_info->pinctrl,
pinctrl_info->wlan_pon_dis);
if (ret) {
icnss_pr_err("state for wlan_pon_dis err=%d\n",
ret);
goto out;
}
}
break;
default:
icnss_pr_err("Unhandled PMIC Pwr sequence %u\n", seq);
}
return 0;
out:
return ret;
}
static int icnss_select_pinctrl_enable(struct icnss_priv *priv)
{
return icnss_pmic_trigger_pon_poff(priv, PMIC_PWR_ON);
}
static int icnss_select_pinctrl_disable(struct icnss_priv *priv)
{
return icnss_pmic_trigger_pon_poff(priv, PMIC_PWR_OFF);
}
int icnss_hw_power_on(struct icnss_priv *priv)
{
int ret = 0;
@ -725,7 +647,9 @@ int icnss_hw_power_on(struct icnss_priv *priv)
goto vreg_off;
if (priv->pon_gpio_control) {
ret = icnss_select_pinctrl_enable(priv);
ret = icnss_power_trigger_pinctrl(&priv->pdev->dev,
ICNSS_PINCTRL_OWNER_WLAN,
ICNSS_PINCTRL_SEQ_ON);
if (ret) {
icnss_pr_err("Failed to select pinctrl state, err = %d\n", ret);
goto clk_off;
@ -769,7 +693,9 @@ int icnss_hw_power_off(struct icnss_priv *priv)
ret = icnss_vreg_off(priv);
if (priv->pon_gpio_control)
ret = icnss_select_pinctrl_disable(priv);
ret = icnss_power_trigger_pinctrl(&priv->pdev->dev,
ICNSS_PINCTRL_OWNER_WLAN,
ICNSS_PINCTRL_SEQ_OFF);
return ret;
}
@ -1039,19 +965,107 @@ int icnss_update_cpr_info(struct icnss_priv *priv)
cpr_info->voltage);
}
int icnss_get_pinctrl(struct icnss_priv *priv)
static int icnss_power_pinctrl_set(struct icnss_priv *priv,
enum icnss_pinctrl_owner owner,
enum icnss_pinctrl_seq seq)
{
int ret = 0;
struct device *dev;
struct icnss_pinctrl_info *pinctrl_info;
if (!priv) {
icnss_pr_pon_seq("plat_priv is NULL!\n");
ret = -ENODEV;
goto out;
}
pinctrl_info = &priv->pinctrl_info;
icnss_pr_pon_seq("icnss: pinctrl seq %u\n", seq);
switch (seq) {
case ICNSS_PINCTRL_SEQ_OFF:
if (!IS_ERR_OR_NULL(pinctrl_info->wlan_poff_en)) {
ret = pinctrl_select_state(pinctrl_info->pinctrl,
pinctrl_info->wlan_poff_en);
if (ret) {
icnss_pr_pon_seq("state for poff_en err=%d\n",
ret);
goto out;
}
} else {
ret = -ENODEV;
goto out;
}
mdelay(WLAN_PON_DELAY);
if (!IS_ERR_OR_NULL(pinctrl_info->wlan_poff_dis)) {
ret = pinctrl_select_state(pinctrl_info->pinctrl,
pinctrl_info->wlan_poff_dis);
if (ret) {
icnss_pr_pon_seq("state for poff_dis err=%d\n",
ret);
goto out;
}
priv->pon_pinctrl_owners &= ~BIT(owner);
priv->pof_pinctrl_owners |= BIT(owner);
} else {
ret = -ENODEV;
goto out;
}
break;
case ICNSS_PINCTRL_SEQ_ON:
if (!IS_ERR_OR_NULL(pinctrl_info->wlan_pon_en)) {
ret = pinctrl_select_state(pinctrl_info->pinctrl,
pinctrl_info->wlan_pon_en);
if (ret) {
icnss_pr_pon_seq("state for pon_en, err=%d\n",
ret);
goto out;
}
} else {
ret = -ENODEV;
goto out;
}
mdelay(WLAN_PON_DELAY);
if (!IS_ERR_OR_NULL(pinctrl_info->wlan_pon_dis)) {
ret = pinctrl_select_state(pinctrl_info->pinctrl,
pinctrl_info->wlan_pon_dis);
if (ret) {
icnss_pr_pon_seq("state for wlan_pon_dis err=%d\n",
ret);
goto out;
}
priv->pon_pinctrl_owners |= BIT(owner);
priv->pof_pinctrl_owners &= ~BIT(owner);
} else {
ret = -ENODEV;
goto out;
}
break;
default:
icnss_pr_pon_seq("Unhandled pinctrl power sequence %u\n", seq);
ret = -EINVAL;
break;
}
out:
return ret;
}
int icnss_get_pinctrl(struct icnss_priv *priv)
{
struct icnss_pinctrl_info *pinctrl_info;
struct device *dev;
int ret = 0;
if (!priv)
return -EINVAL;
/* Init to - no power ons, and all are powered down */
dev = &priv->pdev->dev;
pinctrl_info = &priv->pinctrl_info;
pinctrl_info->pinctrl = devm_pinctrl_get(dev);
if (IS_ERR_OR_NULL(pinctrl_info->pinctrl)) {
ret = PTR_ERR(pinctrl_info->pinctrl);
icnss_pr_err("Failed to get pinctrl, err = %d\n", ret);
icnss_pr_pon_seq("Failed to get pinctrl, err = %d\n", ret);
goto out;
}
@ -1060,8 +1074,7 @@ int icnss_get_pinctrl(struct icnss_priv *priv)
WLAN_PON_EN);
if (IS_ERR_OR_NULL(pinctrl_info->wlan_pon_en)) {
ret = PTR_ERR(pinctrl_info->wlan_pon_en);
icnss_pr_err("Failed to get wlan_pon_en state, err = %d\n",
ret);
icnss_pr_pon_seq("Failed to get pon_en state, err %d\n", ret);
goto out;
}
@ -1070,8 +1083,7 @@ int icnss_get_pinctrl(struct icnss_priv *priv)
WLAN_PON_DIS);
if (IS_ERR_OR_NULL(pinctrl_info->wlan_pon_dis)) {
ret = PTR_ERR(pinctrl_info->wlan_pon_dis);
icnss_pr_err("Failed to get wlan_pon_dis state, err = %d\n",
ret);
icnss_pr_pon_seq("Failed to get pon_dis state, err %d\n", ret);
goto out;
}
@ -1080,8 +1092,7 @@ int icnss_get_pinctrl(struct icnss_priv *priv)
WLAN_POFF_EN);
if (IS_ERR_OR_NULL(pinctrl_info->wlan_poff_en)) {
ret = PTR_ERR(pinctrl_info->wlan_poff_en);
icnss_pr_err("Failed to get wlan_poff_en state, err = %d\n",
ret);
icnss_pr_pon_seq("Failed to get poff_en state, err %d\n", ret);
goto out;
}
@ -1090,8 +1101,7 @@ int icnss_get_pinctrl(struct icnss_priv *priv)
WLAN_POFF_DIS);
if (IS_ERR_OR_NULL(pinctrl_info->wlan_poff_dis)) {
ret = PTR_ERR(pinctrl_info->wlan_poff_dis);
icnss_pr_err("Failed to get wlan_poff_dis state, err = %d\n",
ret);
icnss_pr_pon_seq("Failed to get poff_dis state, er %d\n", ret);
goto out;
}
@ -1099,3 +1109,91 @@ int icnss_get_pinctrl(struct icnss_priv *priv)
out:
return ret;
}
int icnss_power_trigger_pinctrl(struct device *dev,
enum icnss_pinctrl_owner owner,
enum icnss_pinctrl_seq seq)
{
struct icnss_priv *priv = icnss_get_plat_priv();
int retry = 10;
int ret;
u32 all_owners = BIT(ICNSS_PINCTRL_OWNER_WLAN) |
BIT(ICNSS_PINCTRL_OWNER_BT);
if (!priv) {
icnss_pr_pon_seq("icnss2 not initialized");
return -ENODEV;
}
if (!priv->pon_gpio_control) {
icnss_pr_pon_seq("pinctrl_set: PON pinctrl not present");
return 0;
}
if (seq != ICNSS_PINCTRL_SEQ_ON && seq != ICNSS_PINCTRL_SEQ_OFF) {
icnss_pr_pon_seq("Invalid power seq %d", seq);
return -EINVAL;
}
icnss_pr_pon_seq("EPower : seq %d, from %d, pon,pof:0x%x, 0x%x",
seq, owner, priv->pon_pinctrl_owners,
priv->pof_pinctrl_owners);
retry_op:
/* Don't hold the lock for long, check on pon_in_progress instead */
spin_lock(&priv->on_off_lock);
if (priv->pon_in_progress && retry) {
spin_unlock(&priv->on_off_lock);
icnss_pr_pon_seq("Wait for operation to complete");
usleep_range(5000, 10000);
retry--;
goto retry_op;
}
if (!retry && priv->pon_in_progress) {
icnss_pr_pon_seq("Prev operation taking too long to complete");
spin_unlock(&priv->on_off_lock);
return -EINPROGRESS;
}
priv->pon_in_progress = true;
spin_unlock(&priv->on_off_lock);
ret = 0;
/* If PON, and if _this_ owner has not already voted for PON, and none
* of the other owners have triggered PON already, only then trigger
* PON sequence.
*/
if (seq == ICNSS_PINCTRL_SEQ_ON &&
(priv->pon_pinctrl_owners & BIT(owner)) == 0) {
if ((priv->pon_pinctrl_owners & all_owners) == 0) {
ret = icnss_power_pinctrl_set(priv, owner,
ICNSS_PINCTRL_SEQ_ON);
} else {
priv->pon_pinctrl_owners |= BIT(owner);
priv->pof_pinctrl_owners &= ~BIT(owner);
}
goto retrn;
} else if (seq == ICNSS_PINCTRL_SEQ_OFF &&
(priv->pof_pinctrl_owners & BIT(owner)) == 0) {
/* If POF, and if _this_ owner has not already voted for poff, and none
* of the other owners require PON any longer, only then trigger
* POFF sequence.
*/
if ((priv->pon_pinctrl_owners & ~BIT(owner)) == 0) {
ret = icnss_power_pinctrl_set(priv, owner,
ICNSS_PINCTRL_SEQ_OFF);
} else {
priv->pon_pinctrl_owners &= ~BIT(owner);
priv->pof_pinctrl_owners |= BIT(owner);
}
goto retrn;
}
retrn:
icnss_pr_pon_seq("XPower : seq %d, from %d, pon,pof:0x%x, 0x%x",
seq, owner, priv->pon_pinctrl_owners,
priv->pof_pinctrl_owners);
priv->pon_in_progress = false;
return ret;
}
EXPORT_SYMBOL(icnss_power_trigger_pinctrl);

View File

@ -138,6 +138,16 @@ struct icnss_soc_info {
char fw_build_timestamp[ICNSS_MAX_TIMESTAMP_LEN + 1];
};
enum icnss_pinctrl_seq {
ICNSS_PINCTRL_SEQ_OFF,
ICNSS_PINCTRL_SEQ_ON,
};
enum icnss_pinctrl_owner {
ICNSS_PINCTRL_OWNER_WLAN,
ICNSS_PINCTRL_OWNER_BT,
};
#define icnss_register_driver(ops) \
__icnss_register_driver(ops, THIS_MODULE, KBUILD_MODNAME)
extern int __icnss_register_driver(struct icnss_driver_ops *ops,
@ -209,4 +219,7 @@ extern void icnss_allow_l1(struct device *dev);
extern int icnss_get_mhi_state(struct device *dev);
extern int icnss_is_pci_ep_awake(struct device *dev);
extern unsigned long icnss_get_device_config(void);
extern int icnss_power_trigger_pinctrl(struct device *dev,
enum icnss_pinctrl_owner owner,
enum icnss_pinctrl_seq seq);
#endif /* _ICNSS_WLAN_H_ */