@@ -7,6 +7,7 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <net/wilc.h>
+#include "cfg80211.h"
#include "netdev.h"
#include "wlan_if.h"
#include "wlan.h"
@@ -261,22 +262,36 @@ static int wilc_bt_start(struct wilc *wilc)
int wilc_bt_init(void *wilc_wl_priv)
{
struct wilc *wilc = (struct wilc *)wilc_wl_priv;
+ struct wilc_vif *vif;
int ret;
+ wilc->bt_enabled = true;
+
if (!wilc->hif_func->hif_is_init(wilc)) {
dev_info(wilc->dev, "Initializing bus before starting BT");
acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY);
ret = wilc->hif_func->hif_init(wilc, false);
release_bus(wilc, WILC_BUS_RELEASE_ONLY);
- if (ret)
+ if (ret) {
+ wilc->bt_enabled = false;
return ret;
+ }
}
+ /* Power save feature managed by WLAN firmware may disrupt
+ * operations from the bluetooth CPU, so disable it while bluetooth
+ * is in use (if enabled, it will be enabled back when bluetooth is
+ * not used anymore)
+ */
+ vif = wilc_get_wl_to_vif(wilc);
+ if (wilc->power_save_mode && wilc_set_power_mgmt(vif, false))
+ goto hif_deinit;
+
mutex_lock(&wilc->radio_fw_start);
ret = wilc_bt_power_up(wilc);
if (ret) {
dev_err(wilc->dev, "Error powering up bluetooth chip\n");
- goto hif_deinit;
+ goto reenable_power_save;
}
ret = wilc_bt_firmware_download(wilc);
if (ret) {
@@ -293,10 +308,14 @@ int wilc_bt_init(void *wilc_wl_priv)
power_down:
wilc_bt_power_down(wilc);
-hif_deinit:
+reenable_power_save:
+ if (wilc->power_save_mode_request)
+ wilc_set_power_mgmt(vif, true);
mutex_unlock(&wilc->radio_fw_start);
+hif_deinit:
if (!wilc->initialized)
wilc->hif_func->hif_deinit(wilc);
+ wilc->bt_enabled = false;
return ret;
}
EXPORT_SYMBOL(wilc_bt_init);
@@ -304,6 +323,7 @@ EXPORT_SYMBOL(wilc_bt_init);
int wilc_bt_shutdown(void *wilc_wl_priv)
{
struct wilc *wilc = (struct wilc *)wilc_wl_priv;
+ struct wilc_vif *vif;
int ret;
mutex_lock(&wilc->radio_fw_start);
@@ -313,6 +333,9 @@ int wilc_bt_shutdown(void *wilc_wl_priv)
dev_warn(wilc->dev, "Failed to disable BT CPU\n");
if (wilc_bt_power_down(wilc))
dev_warn(wilc->dev, "Failed to power down BT CPU\n");
+ vif = wilc_get_wl_to_vif(wilc);
+ if (wilc->power_save_mode_request && wilc_set_power_mgmt(vif, true))
+ dev_warn(wilc->dev, "Failed to set back wlan power save\n");
if (!wilc->initialized)
wilc->hif_func->hif_deinit(wilc);
mutex_unlock(&wilc->radio_fw_start);
@@ -1344,11 +1344,14 @@ static int set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
{
struct wilc_vif *vif = netdev_priv(dev);
struct wilc_priv *priv = &vif->priv;
+ struct wilc *wilc = vif->wilc;
if (!priv->hif_drv)
return -EIO;
- wilc_set_power_mgmt(vif, enabled);
+ wilc->power_save_mode_request = enabled;
+ if (!wilc->bt_enabled)
+ wilc_set_power_mgmt(vif, enabled);
return 0;
}
@@ -213,6 +213,7 @@ struct wilc {
struct clk *rtc_clk;
bool initialized;
u32 chipid;
+ bool power_save_mode_request;
bool power_save_mode;
int dev_irq_num;
int close;
@@ -289,6 +290,8 @@ struct wilc {
u8 nv_mac_address[ETH_ALEN];
/* Lock to prevent concurrent start of wlan/bluetooth firmware */
struct mutex radio_fw_start;
+ /* Is the bluetooth part in use ? */
+ bool bt_enabled;
};
struct wilc_wfi_mon_priv {
If the wlan interface exposed by wilc driver has power save enabled (either explicitly with iw dev wlan set power_save on, or because kernel is built with CONFIG_CFG80211_DEFAULT_PS), it will send a power management command to the wlan firmware when corresponding interface is brought up. The bluetooth part, if used, is supposed to work independently from the WLAN CPU. Unfortunately, this power save management, if applied by the WLAN side, disrupts bluetooth operations (the bluetooth CPU does not answer any command anymore on the UART interface) Make sure that the bluetooth part can work independently by disabling power save in wlan firmware when bluetooth is in use. Signed-off-by: Alexis Lothoré <alexis.lothore@bootlin.com> --- drivers/net/wireless/microchip/wilc1000/bt.c | 29 +++++++++++++++++++--- drivers/net/wireless/microchip/wilc1000/cfg80211.c | 5 +++- drivers/net/wireless/microchip/wilc1000/netdev.h | 3 +++ 3 files changed, 33 insertions(+), 4 deletions(-)