cnss2: Support runtime power on/off WLAN device
WLAN host driver may do runtime mode switch which requires platform driver to reset device and re-download firmware. Add the changes for runtime power on/off WLAN device to support runtime mode switch. Change-Id: I08d3635afbaafb73691fbdf5d25877fb433bae18 CRs-fixed: 2059087 Signed-off-by: Yue Ma <yuem@codeaurora.org>
This commit is contained in:
parent
0ea2b1fc15
commit
f2573341c1
2 changed files with 99 additions and 65 deletions
|
@ -573,7 +573,7 @@ static int cnss_driver_call_probe(struct cnss_plat_data *plat_priv)
|
||||||
struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
|
struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
|
||||||
|
|
||||||
if (!plat_priv->driver_ops) {
|
if (!plat_priv->driver_ops) {
|
||||||
cnss_pr_err("driver_ops is NULL!");
|
cnss_pr_err("driver_ops is NULL\n");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -587,7 +587,7 @@ static int cnss_driver_call_probe(struct cnss_plat_data *plat_priv)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state);
|
clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state);
|
||||||
} else {
|
} else if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state)) {
|
||||||
ret = plat_priv->driver_ops->probe(pci_priv->pci_dev,
|
ret = plat_priv->driver_ops->probe(pci_priv->pci_dev,
|
||||||
pci_priv->pci_device_id);
|
pci_priv->pci_device_id);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -605,6 +605,25 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cnss_driver_call_remove(struct cnss_plat_data *plat_priv)
|
||||||
|
{
|
||||||
|
struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
|
||||||
|
|
||||||
|
if (!plat_priv->driver_ops) {
|
||||||
|
cnss_pr_err("driver_ops is NULL\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) {
|
||||||
|
plat_priv->driver_ops->shutdown(pci_priv->pci_dev);
|
||||||
|
} else if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) {
|
||||||
|
plat_priv->driver_ops->remove(pci_priv->pci_dev);
|
||||||
|
clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv)
|
static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -626,8 +645,11 @@ static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv)
|
||||||
} else if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state)) {
|
} else if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state)) {
|
||||||
ret = cnss_wlfw_wlan_mode_send_sync(plat_priv,
|
ret = cnss_wlfw_wlan_mode_send_sync(plat_priv,
|
||||||
QMI_WLFW_CALIBRATION_V01);
|
QMI_WLFW_CALIBRATION_V01);
|
||||||
} else {
|
} else if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state) ||
|
||||||
|
test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) {
|
||||||
ret = cnss_driver_call_probe(plat_priv);
|
ret = cnss_driver_call_probe(plat_priv);
|
||||||
|
} else {
|
||||||
|
complete(&plat_priv->power_up_complete);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -668,6 +690,10 @@ static char *cnss_driver_event_to_str(enum cnss_driver_event_type type)
|
||||||
return "RECOVERY";
|
return "RECOVERY";
|
||||||
case CNSS_DRIVER_EVENT_FORCE_FW_ASSERT:
|
case CNSS_DRIVER_EVENT_FORCE_FW_ASSERT:
|
||||||
return "FORCE_FW_ASSERT";
|
return "FORCE_FW_ASSERT";
|
||||||
|
case CNSS_DRIVER_EVENT_POWER_UP:
|
||||||
|
return "POWER_UP";
|
||||||
|
case CNSS_DRIVER_EVENT_POWER_DOWN:
|
||||||
|
return "POWER_DOWN";
|
||||||
case CNSS_DRIVER_EVENT_MAX:
|
case CNSS_DRIVER_EVENT_MAX:
|
||||||
return "EVENT_MAX";
|
return "EVENT_MAX";
|
||||||
}
|
}
|
||||||
|
@ -746,64 +772,57 @@ out:
|
||||||
int cnss_power_up(struct device *dev)
|
int cnss_power_up(struct device *dev)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
void *bus_priv = cnss_bus_dev_to_bus_priv(dev);
|
|
||||||
struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
|
struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
|
||||||
|
unsigned int timeout;
|
||||||
|
|
||||||
if (!bus_priv || !plat_priv)
|
if (!plat_priv) {
|
||||||
|
cnss_pr_err("plat_priv is NULL\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
if (plat_priv->device_id != QCA6174_DEVICE_ID) {
|
|
||||||
cnss_pr_dbg("Power up is not supported for device ID 0x%lx\n",
|
|
||||||
plat_priv->device_id);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = cnss_power_on_device(plat_priv);
|
cnss_pr_dbg("Powering up device\n");
|
||||||
if (ret) {
|
|
||||||
cnss_pr_err("Failed to power on device, err = %d\n", ret);
|
|
||||||
goto err_power_on;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = cnss_resume_pci_link(bus_priv);
|
ret = cnss_driver_event_post(plat_priv,
|
||||||
if (ret) {
|
CNSS_DRIVER_EVENT_POWER_UP,
|
||||||
cnss_pr_err("Failed to resume PCI link, err = %d\n", ret);
|
true, NULL);
|
||||||
goto err_resume_link;
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (plat_priv->device_id == QCA6174_DEVICE_ID)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
timeout = cnss_get_qmi_timeout();
|
||||||
|
|
||||||
|
reinit_completion(&plat_priv->power_up_complete);
|
||||||
|
ret = wait_for_completion_timeout(&plat_priv->power_up_complete,
|
||||||
|
msecs_to_jiffies(timeout) << 2);
|
||||||
|
if (!ret) {
|
||||||
|
cnss_pr_err("Timeout waiting for power up to complete\n");
|
||||||
|
ret = -EAGAIN;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
err_resume_link:
|
|
||||||
cnss_power_off_device(plat_priv);
|
out:
|
||||||
err_power_on:
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(cnss_power_up);
|
EXPORT_SYMBOL(cnss_power_up);
|
||||||
|
|
||||||
int cnss_power_down(struct device *dev)
|
int cnss_power_down(struct device *dev)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
|
||||||
void *bus_priv = cnss_bus_dev_to_bus_priv(dev);
|
|
||||||
struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
|
struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
|
||||||
|
|
||||||
if (!bus_priv || !plat_priv)
|
if (!plat_priv) {
|
||||||
|
cnss_pr_err("plat_priv is NULL\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
if (plat_priv->device_id != QCA6174_DEVICE_ID) {
|
|
||||||
cnss_pr_dbg("Power down is not supported for device ID 0x%lx\n",
|
|
||||||
plat_priv->device_id);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE);
|
cnss_pr_dbg("Powering down device\n");
|
||||||
cnss_pci_set_monitor_wake_intr(bus_priv, false);
|
|
||||||
cnss_pci_set_auto_suspended(bus_priv, 0);
|
|
||||||
|
|
||||||
ret = cnss_suspend_pci_link(bus_priv);
|
return cnss_driver_event_post(plat_priv,
|
||||||
if (ret)
|
CNSS_DRIVER_EVENT_POWER_DOWN,
|
||||||
cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret);
|
true, NULL);
|
||||||
|
|
||||||
cnss_power_off_device(plat_priv);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(cnss_power_down);
|
EXPORT_SYMBOL(cnss_power_down);
|
||||||
|
|
||||||
|
@ -1024,14 +1043,11 @@ static int cnss_qca6174_shutdown(struct cnss_plat_data *plat_priv)
|
||||||
if (!plat_priv->driver_ops)
|
if (!plat_priv->driver_ops)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) {
|
cnss_driver_call_remove(plat_priv);
|
||||||
|
|
||||||
cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE);
|
cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE);
|
||||||
plat_priv->driver_ops->remove(pci_priv->pci_dev);
|
|
||||||
cnss_pci_set_monitor_wake_intr(pci_priv, false);
|
cnss_pci_set_monitor_wake_intr(pci_priv, false);
|
||||||
cnss_pci_set_auto_suspended(pci_priv, 0);
|
cnss_pci_set_auto_suspended(pci_priv, 0);
|
||||||
} else {
|
|
||||||
plat_priv->driver_ops->shutdown(pci_priv->pci_dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = cnss_suspend_pci_link(pci_priv);
|
ret = cnss_suspend_pci_link(pci_priv);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -1039,10 +1055,7 @@ static int cnss_qca6174_shutdown(struct cnss_plat_data *plat_priv)
|
||||||
|
|
||||||
cnss_power_off_device(plat_priv);
|
cnss_power_off_device(plat_priv);
|
||||||
|
|
||||||
if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) {
|
|
||||||
clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state);
|
clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state);
|
||||||
clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1134,16 +1147,13 @@ static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv)
|
||||||
if (!plat_priv->driver_ops)
|
if (!plat_priv->driver_ops)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) {
|
cnss_driver_call_remove(plat_priv);
|
||||||
cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE);
|
|
||||||
plat_priv->driver_ops->remove(pci_priv->pci_dev);
|
|
||||||
cnss_pci_set_monitor_wake_intr(pci_priv, false);
|
|
||||||
cnss_pci_set_auto_suspended(pci_priv, 0);
|
|
||||||
} else {
|
|
||||||
plat_priv->driver_ops->shutdown(pci_priv->pci_dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
skip_driver_remove:
|
skip_driver_remove:
|
||||||
|
cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_NONE);
|
||||||
|
cnss_pci_set_monitor_wake_intr(pci_priv, false);
|
||||||
|
cnss_pci_set_auto_suspended(pci_priv, 0);
|
||||||
|
|
||||||
cnss_pci_stop_mhi(pci_priv);
|
cnss_pci_stop_mhi(pci_priv);
|
||||||
|
|
||||||
ret = cnss_suspend_pci_link(pci_priv);
|
ret = cnss_suspend_pci_link(pci_priv);
|
||||||
|
@ -1154,11 +1164,7 @@ skip_driver_remove:
|
||||||
|
|
||||||
clear_bit(CNSS_FW_READY, &plat_priv->driver_state);
|
clear_bit(CNSS_FW_READY, &plat_priv->driver_state);
|
||||||
clear_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state);
|
clear_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state);
|
||||||
|
|
||||||
if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) {
|
|
||||||
clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state);
|
clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state);
|
||||||
clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1661,6 +1667,22 @@ static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cnss_power_up_hdlr(struct cnss_plat_data *plat_priv)
|
||||||
|
{
|
||||||
|
struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info;
|
||||||
|
|
||||||
|
return cnss_powerup(&subsys_info->subsys_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cnss_power_down_hdlr(struct cnss_plat_data *plat_priv)
|
||||||
|
{
|
||||||
|
struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info;
|
||||||
|
|
||||||
|
cnss_shutdown(&subsys_info->subsys_desc, false);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void cnss_driver_event_work(struct work_struct *work)
|
static void cnss_driver_event_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct cnss_plat_data *plat_priv =
|
struct cnss_plat_data *plat_priv =
|
||||||
|
@ -1728,6 +1750,12 @@ static void cnss_driver_event_work(struct work_struct *work)
|
||||||
case CNSS_DRIVER_EVENT_FORCE_FW_ASSERT:
|
case CNSS_DRIVER_EVENT_FORCE_FW_ASSERT:
|
||||||
ret = cnss_force_fw_assert_hdlr(plat_priv);
|
ret = cnss_force_fw_assert_hdlr(plat_priv);
|
||||||
break;
|
break;
|
||||||
|
case CNSS_DRIVER_EVENT_POWER_UP:
|
||||||
|
ret = cnss_power_up_hdlr(plat_priv);
|
||||||
|
break;
|
||||||
|
case CNSS_DRIVER_EVENT_POWER_DOWN:
|
||||||
|
ret = cnss_power_down_hdlr(plat_priv);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
cnss_pr_err("Invalid driver event type: %d",
|
cnss_pr_err("Invalid driver event type: %d",
|
||||||
event->type);
|
event->type);
|
||||||
|
@ -2226,6 +2254,8 @@ static int cnss_probe(struct platform_device *plat_dev)
|
||||||
cnss_pr_err("Failed to init platform device wakeup source, err = %d\n",
|
cnss_pr_err("Failed to init platform device wakeup source, err = %d\n",
|
||||||
ret);
|
ret);
|
||||||
|
|
||||||
|
init_completion(&plat_priv->power_up_complete);
|
||||||
|
|
||||||
cnss_pr_info("Platform driver probed successfully.\n");
|
cnss_pr_info("Platform driver probed successfully.\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2257,6 +2287,7 @@ static int cnss_remove(struct platform_device *plat_dev)
|
||||||
{
|
{
|
||||||
struct cnss_plat_data *plat_priv = platform_get_drvdata(plat_dev);
|
struct cnss_plat_data *plat_priv = platform_get_drvdata(plat_dev);
|
||||||
|
|
||||||
|
complete_all(&plat_priv->power_up_complete);
|
||||||
device_init_wakeup(&plat_dev->dev, false);
|
device_init_wakeup(&plat_dev->dev, false);
|
||||||
unregister_pm_notifier(&cnss_pm_notifier);
|
unregister_pm_notifier(&cnss_pm_notifier);
|
||||||
del_timer(&plat_priv->fw_boot_timer);
|
del_timer(&plat_priv->fw_boot_timer);
|
||||||
|
|
|
@ -126,6 +126,8 @@ enum cnss_driver_event_type {
|
||||||
CNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
|
CNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
|
||||||
CNSS_DRIVER_EVENT_RECOVERY,
|
CNSS_DRIVER_EVENT_RECOVERY,
|
||||||
CNSS_DRIVER_EVENT_FORCE_FW_ASSERT,
|
CNSS_DRIVER_EVENT_FORCE_FW_ASSERT,
|
||||||
|
CNSS_DRIVER_EVENT_POWER_UP,
|
||||||
|
CNSS_DRIVER_EVENT_POWER_DOWN,
|
||||||
CNSS_DRIVER_EVENT_MAX,
|
CNSS_DRIVER_EVENT_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -201,6 +203,7 @@ struct cnss_plat_data {
|
||||||
struct dentry *root_dentry;
|
struct dentry *root_dentry;
|
||||||
atomic_t pm_count;
|
atomic_t pm_count;
|
||||||
struct timer_list fw_boot_timer;
|
struct timer_list fw_boot_timer;
|
||||||
|
struct completion power_up_complete;
|
||||||
};
|
};
|
||||||
|
|
||||||
void *cnss_bus_dev_to_bus_priv(struct device *dev);
|
void *cnss_bus_dev_to_bus_priv(struct device *dev);
|
||||||
|
|
Loading…
Add table
Reference in a new issue