| Index: chromeos/drivers/ath6kl/os/linux/ar6000_pm.c
|
| diff --git a/chromeos/drivers/ath6kl/os/linux/ar6000_pm.c b/chromeos/drivers/ath6kl/os/linux/ar6000_pm.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b937df9c0cb5f6899030aa0dce2ce5ad17788c4b
|
| --- /dev/null
|
| +++ b/chromeos/drivers/ath6kl/os/linux/ar6000_pm.c
|
| @@ -0,0 +1,731 @@
|
| +/*
|
| + *
|
| + * Copyright (c) 2004-2010 Atheros Communications Inc.
|
| + * All rights reserved.
|
| + *
|
| + *
|
| +//
|
| +// Permission to use, copy, modify, and/or distribute this software for any
|
| +// purpose with or without fee is hereby granted, provided that the above
|
| +// copyright notice and this permission notice appear in all copies.
|
| +//
|
| +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
| +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
| +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
| +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
| +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
| +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
| +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
| +//
|
| +//
|
| + *
|
| + */
|
| +
|
| +/*
|
| + * Implementation of system power management
|
| + */
|
| +
|
| +#include "ar6000_drv.h"
|
| +#include <linux/inetdevice.h>
|
| +#include <linux/platform_device.h>
|
| +#include "wlan_config.h"
|
| +
|
| +#ifdef CONFIG_HAS_WAKELOCK
|
| +#include <linux/wakelock.h>
|
| +#endif
|
| +
|
| +#define WOW_ENABLE_MAX_INTERVAL 0
|
| +#define WOW_SET_SCAN_PARAMS 0
|
| +
|
| +extern unsigned int wmitimeout;
|
| +extern wait_queue_head_t arEvent;
|
| +
|
| +#ifdef CONFIG_PM
|
| +#ifdef CONFIG_HAS_WAKELOCK
|
| +struct wake_lock ar6k_suspend_wake_lock;
|
| +struct wake_lock ar6k_wow_wake_lock;
|
| +#endif
|
| +#endif /* CONFIG_PM */
|
| +
|
| +#ifdef ANDROID_ENV
|
| +extern void android_ar6k_check_wow_status(AR_SOFTC_T *ar, struct sk_buff *skb, A_BOOL isEvent);
|
| +#endif
|
| +#undef ATH_MODULE_NAME
|
| +#define ATH_MODULE_NAME pm
|
| +#define ATH_DEBUG_PM ATH_DEBUG_MAKE_MODULE_MASK(0)
|
| +
|
| +#ifdef DEBUG
|
| +static ATH_DEBUG_MASK_DESCRIPTION pm_debug_desc[] = {
|
| + { ATH_DEBUG_PM , "System power management"},
|
| +};
|
| +
|
| +ATH_DEBUG_INSTANTIATE_MODULE_VAR(pm,
|
| + "pm",
|
| + "System Power Management",
|
| + ATH_DEBUG_MASK_DEFAULTS | ATH_DEBUG_PM,
|
| + ATH_DEBUG_DESCRIPTION_COUNT(pm_debug_desc),
|
| + pm_debug_desc);
|
| +
|
| +#endif /* DEBUG */
|
| +
|
| +A_STATUS ar6000_exit_cut_power_state(AR_SOFTC_T *ar);
|
| +
|
| +#ifdef CONFIG_PM
|
| +static void ar6k_send_asleep_event_to_app(AR_SOFTC_T *ar, A_BOOL asleep)
|
| +{
|
| + char buf[128];
|
| + union iwreq_data wrqu;
|
| +
|
| + snprintf(buf, sizeof(buf), "HOST_ASLEEP=%s", asleep ? "asleep" : "awake");
|
| + A_MEMZERO(&wrqu, sizeof(wrqu));
|
| + wrqu.data.length = strlen(buf);
|
| + wireless_send_event(ar->arNetDev, IWEVCUSTOM, &wrqu, buf);
|
| +}
|
| +
|
| +static void ar6000_wow_resume(AR_SOFTC_T *ar)
|
| +{
|
| + if (ar->arWowState!= WLAN_WOW_STATE_NONE) {
|
| + A_UINT16 fg_start_period = (ar->scParams.fg_start_period==0) ? 1 : ar->scParams.fg_start_period;
|
| + A_UINT16 bg_period = (ar->scParams.bg_period==0) ? 60 : ar->scParams.bg_period;
|
| + WMI_SET_HOST_SLEEP_MODE_CMD hostSleepMode = {TRUE, FALSE};
|
| + ar->arWowState = WLAN_WOW_STATE_NONE;
|
| +#ifdef CONFIG_HAS_WAKELOCK
|
| + wake_lock_timeout(&ar6k_wow_wake_lock, 3*HZ);
|
| +#endif
|
| + if (wmi_set_host_sleep_mode_cmd(ar->arWmi, &hostSleepMode)!=A_OK) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Fail to setup restore host awake\n"));
|
| + }
|
| +#if WOW_SET_SCAN_PARAMS
|
| + wmi_scanparams_cmd(ar->arWmi, fg_start_period,
|
| + ar->scParams.fg_end_period,
|
| + bg_period,
|
| + ar->scParams.minact_chdwell_time,
|
| + ar->scParams.maxact_chdwell_time,
|
| + ar->scParams.pas_chdwell_time,
|
| + ar->scParams.shortScanRatio,
|
| + ar->scParams.scanCtrlFlags,
|
| + ar->scParams.max_dfsch_act_time,
|
| + ar->scParams.maxact_scan_per_ssid);
|
| +#else
|
| + (void)fg_start_period;
|
| + (void)bg_period;
|
| +#endif
|
| +
|
| +
|
| +#if WOW_ENABLE_MAX_INTERVAL /* we don't do it if the power consumption is already good enough. */
|
| + if (wmi_listeninterval_cmd(ar->arWmi, ar->arListenIntervalT, ar->arListenIntervalB) == A_OK) {
|
| + }
|
| +#endif
|
| + ar6k_send_asleep_event_to_app(ar, FALSE);
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM, ("Resume WoW successfully\n"));
|
| + } else {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM, ("WoW does not invoked. skip resume"));
|
| + }
|
| + ar->arWlanPowerState = WLAN_POWER_STATE_ON;
|
| +}
|
| +
|
| +static void ar6000_wow_suspend(AR_SOFTC_T *ar)
|
| +{
|
| +#define WOW_LIST_ID 1
|
| + if (ar->arNetworkType != AP_NETWORK) {
|
| + /* Setup WoW for unicast & Arp request for our own IP
|
| + disable background scan. Set listen interval into 1000 TUs
|
| + Enable keepliave for 110 seconds
|
| + */
|
| + struct in_ifaddr **ifap = NULL;
|
| + struct in_ifaddr *ifa = NULL;
|
| + struct in_device *in_dev;
|
| + A_UINT8 macMask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
| + A_STATUS status;
|
| + WMI_ADD_WOW_PATTERN_CMD addWowCmd = { .filter = { 0 } };
|
| + WMI_DEL_WOW_PATTERN_CMD delWowCmd;
|
| + WMI_SET_HOST_SLEEP_MODE_CMD hostSleepMode = {FALSE, TRUE};
|
| + WMI_SET_WOW_MODE_CMD wowMode = { .enable_wow = TRUE,
|
| + .hostReqDelay = 500 };/*500 ms delay*/
|
| +
|
| + if (ar->arWowState!= WLAN_WOW_STATE_NONE) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("System already go into wow mode!\n"));
|
| + return;
|
| + }
|
| +
|
| + ar6000_TxDataCleanup(ar); /* IMPORTANT, otherwise there will be 11mA after listen interval as 1000*/
|
| +
|
| +#if WOW_ENABLE_MAX_INTERVAL /* we don't do it if the power consumption is already good enough. */
|
| + if (wmi_listeninterval_cmd(ar->arWmi, A_MAX_WOW_LISTEN_INTERVAL, 0) == A_OK) {
|
| + }
|
| +#endif
|
| +
|
| +#if WOW_SET_SCAN_PARAMS
|
| + status = wmi_scanparams_cmd(ar->arWmi, 0xFFFF, 0, 0xFFFF, 0, 0, 0, 0, 0, 0, 0);
|
| +#endif
|
| + /* clear up our WoW pattern first */
|
| + delWowCmd.filter_list_id = WOW_LIST_ID;
|
| + delWowCmd.filter_id = 0;
|
| + wmi_del_wow_pattern_cmd(ar->arWmi, &delWowCmd);
|
| +
|
| + /* setup unicast packet pattern for WoW */
|
| + if (ar->arNetDev->dev_addr[1]) {
|
| + addWowCmd.filter_list_id = WOW_LIST_ID;
|
| + addWowCmd.filter_size = 6; /* MAC address */
|
| + addWowCmd.filter_offset = 0;
|
| + status = wmi_add_wow_pattern_cmd(ar->arWmi, &addWowCmd, ar->arNetDev->dev_addr, macMask, addWowCmd.filter_size);
|
| + if (status != A_OK) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Fail to add WoW pattern\n"));
|
| + }
|
| + }
|
| + /* setup ARP request for our own IP */
|
| + if ((in_dev = __in_dev_get_rtnl(ar->arNetDev)) != NULL) {
|
| + for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; ifap = &ifa->ifa_next) {
|
| + if (!strcmp(ar->arNetDev->name, ifa->ifa_label)) {
|
| + break; /* found */
|
| + }
|
| + }
|
| + }
|
| + if (ifa && ifa->ifa_local) {
|
| + WMI_SET_IP_CMD ipCmd;
|
| + memset(&ipCmd, 0, sizeof(ipCmd));
|
| + ipCmd.ips[0] = ifa->ifa_local;
|
| + status = wmi_set_ip_cmd(ar->arWmi, &ipCmd);
|
| + if (status != A_OK) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Fail to setup IP for ARP agent\n"));
|
| + }
|
| + }
|
| +
|
| +#ifndef ATH6K_CONFIG_OTA_MODE
|
| + wmi_powermode_cmd(ar->arWmi, REC_POWER);
|
| +#endif
|
| +
|
| + status = wmi_set_wow_mode_cmd(ar->arWmi, &wowMode);
|
| + if (status != A_OK) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Fail to enable wow mode\n"));
|
| + }
|
| + ar6k_send_asleep_event_to_app(ar, TRUE);
|
| +
|
| + status = wmi_set_host_sleep_mode_cmd(ar->arWmi, &hostSleepMode);
|
| + if (status != A_OK) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Fail to set host asleep\n"));
|
| + }
|
| +
|
| + ar->arWowState = WLAN_WOW_STATE_SUSPENDING;
|
| + if (ar->arTxPending[ar->arControlEp]) {
|
| + A_UINT32 timeleft = wait_event_interruptible_timeout(arEvent,
|
| + ar->arTxPending[ar->arControlEp] == 0, wmitimeout * HZ);
|
| + if (!timeleft || signal_pending(current)) {
|
| + /* what can I do? wow resume at once */
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Fail to setup WoW. Pending wmi control data %d\n", ar->arTxPending[ar->arControlEp]));
|
| + }
|
| + }
|
| +
|
| + status = hifWaitForPendingRecv(ar->arHifDevice);
|
| +
|
| + ar->arWowState = WLAN_WOW_STATE_SUSPENDED;
|
| + ar->arWlanPowerState = WLAN_POWER_STATE_WOW;
|
| + } else {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Not allowed to go to WOW at this moment.\n"));
|
| + }
|
| +}
|
| +
|
| +A_STATUS ar6000_suspend_ev(void *context)
|
| +{
|
| + A_STATUS status = A_OK;
|
| + AR_SOFTC_T *ar = (AR_SOFTC_T *)context;
|
| + A_INT16 pmmode = ar->arSuspendConfig;
|
| +wow_not_connected:
|
| + switch (pmmode) {
|
| + case WLAN_SUSPEND_WOW:
|
| + if (ar->arWmiReady && ar->arWlanState==WLAN_ENABLED && ar->arConnected) {
|
| + ar6000_wow_suspend(ar);
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM,("%s:Suspend for wow mode %d\n", __func__, ar->arWlanPowerState));
|
| + } else {
|
| + pmmode = ar->arWow2Config;
|
| + goto wow_not_connected;
|
| + }
|
| + break;
|
| + case WLAN_SUSPEND_CUT_PWR:
|
| + /* fall through */
|
| + case WLAN_SUSPEND_CUT_PWR_IF_BT_OFF:
|
| + /* fall through */
|
| + case WLAN_SUSPEND_DEEP_SLEEP:
|
| + /* fall through */
|
| + default:
|
| + status = ar6000_update_wlan_pwr_state(ar, WLAN_DISABLED, TRUE);
|
| + if (ar->arWlanPowerState==WLAN_POWER_STATE_ON ||
|
| + ar->arWlanPowerState==WLAN_POWER_STATE_WOW) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM, ("Strange suspend state for not wow mode %d", ar->arWlanPowerState));
|
| + }
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM,("%s:Suspend for %d mode pwr %d status %d\n", __func__, pmmode, ar->arWlanPowerState, status));
|
| + status = (ar->arWlanPowerState == WLAN_POWER_STATE_CUT_PWR) ? A_OK : A_EBUSY;
|
| + break;
|
| + }
|
| +
|
| + ar->scan_triggered = 0;
|
| + return status;
|
| +}
|
| +
|
| +A_STATUS ar6000_resume_ev(void *context)
|
| +{
|
| + AR_SOFTC_T *ar = (AR_SOFTC_T *)context;
|
| + A_UINT16 powerState = ar->arWlanPowerState;
|
| +
|
| +#ifdef CONFIG_HAS_WAKELOCK
|
| + wake_lock(&ar6k_suspend_wake_lock);
|
| +#endif
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM, ("%s: enter previous state %d wowState %d\n", __func__, powerState, ar->arWowState));
|
| + switch (powerState) {
|
| + case WLAN_POWER_STATE_WOW:
|
| + ar6000_wow_resume(ar);
|
| + break;
|
| + case WLAN_POWER_STATE_CUT_PWR:
|
| + /* fall through */
|
| + case WLAN_POWER_STATE_DEEP_SLEEP:
|
| + ar6000_update_wlan_pwr_state(ar, WLAN_ENABLED, TRUE);
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM,("%s:Resume for %d mode pwr %d\n", __func__, powerState, ar->arWlanPowerState));
|
| + break;
|
| + case WLAN_POWER_STATE_ON:
|
| + break;
|
| + default:
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Strange SDIO bus power mode!!\n"));
|
| + break;
|
| + }
|
| +#ifdef CONFIG_HAS_WAKELOCK
|
| + wake_unlock(&ar6k_suspend_wake_lock);
|
| +#endif
|
| + return A_OK;
|
| +}
|
| +
|
| +void ar6000_check_wow_status(AR_SOFTC_T *ar, struct sk_buff *skb, A_BOOL isEvent)
|
| +{
|
| + if (ar->arWowState!=WLAN_WOW_STATE_NONE) {
|
| + if (ar->arWowState==WLAN_WOW_STATE_SUSPENDING) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM,("\n%s: Received IRQ while we are wow suspending!!!\n\n", __func__));
|
| + return;
|
| + }
|
| + /* Wow resume from irq interrupt */
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM, ("%s: WoW resume from irq thread status %d\n", __func__, ar->arWlanPowerState));
|
| + ar6000_wow_resume(ar);
|
| + } else {
|
| +#ifdef ANDROID_ENV
|
| + android_ar6k_check_wow_status(ar, skb, isEvent);
|
| +#endif
|
| + }
|
| +}
|
| +
|
| +A_STATUS ar6000_power_change_ev(void *context, A_UINT32 config)
|
| +{
|
| + AR_SOFTC_T *ar = (AR_SOFTC_T *)context;
|
| + A_STATUS status = A_OK;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM, ("%s: power change event callback %d \n", __func__, config));
|
| + switch (config) {
|
| + case HIF_DEVICE_POWER_UP:
|
| + ar6000_restart_endpoint(ar->arNetDev);
|
| + status = A_OK;
|
| + break;
|
| + case HIF_DEVICE_POWER_DOWN:
|
| + case HIF_DEVICE_POWER_CUT:
|
| + status = A_OK;
|
| + break;
|
| + }
|
| + return status;
|
| +}
|
| +
|
| +static int ar6000_pm_probe(struct platform_device *pdev)
|
| +{
|
| + plat_setup_power(1,1);
|
| + return 0;
|
| +}
|
| +
|
| +static int ar6000_pm_remove(struct platform_device *pdev)
|
| +{
|
| + plat_setup_power(0,1);
|
| + return 0;
|
| +}
|
| +
|
| +static int ar6000_pm_suspend(struct platform_device *pdev, pm_message_t state)
|
| +{
|
| + return 0;
|
| +}
|
| +
|
| +static int ar6000_pm_resume(struct platform_device *pdev)
|
| +{
|
| + return 0;
|
| +}
|
| +
|
| +static struct platform_driver ar6000_pm_device = {
|
| + .probe = ar6000_pm_probe,
|
| + .remove = ar6000_pm_remove,
|
| + .suspend = ar6000_pm_suspend,
|
| + .resume = ar6000_pm_resume,
|
| + .driver = {
|
| + .name = "wlan_ar6000_pm",
|
| + },
|
| +};
|
| +#endif /* CONFIG_PM */
|
| +
|
| +A_STATUS
|
| +ar6000_setup_cut_power_state(struct ar6_softc *ar, AR6000_WLAN_STATE state)
|
| +{
|
| + A_STATUS status = A_OK;
|
| + HIF_DEVICE_POWER_CHANGE_TYPE config;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM, ("%s: Cut power %d %d \n", __func__,state, ar->arWlanPowerState));
|
| +#ifdef CONFIG_PM
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM, ("Wlan OFF %d BT OFf %d \n", ar->arWlanOff, ar->arBTOff));
|
| +#endif
|
| + do {
|
| + if (state == WLAN_ENABLED) {
|
| + /* Not in cut power state.. exit */
|
| + if (ar->arWlanPowerState != WLAN_POWER_STATE_CUT_PWR) {
|
| + break;
|
| + }
|
| +
|
| + plat_setup_power(1,0);
|
| +
|
| + /* Change the state to ON */
|
| + ar->arWlanPowerState = WLAN_POWER_STATE_ON;
|
| +
|
| +
|
| + /* Indicate POWER_UP to HIF */
|
| + config = HIF_DEVICE_POWER_UP;
|
| + status = HIFConfigureDevice(ar->arHifDevice,
|
| + HIF_DEVICE_POWER_STATE_CHANGE,
|
| + &config,
|
| + sizeof(HIF_DEVICE_POWER_CHANGE_TYPE));
|
| +
|
| + if (status == A_PENDING) {
|
| +#ifdef ANDROID_ENV
|
| + /* Wait for WMI ready event */
|
| + A_UINT32 timeleft = wait_event_interruptible_timeout(arEvent,
|
| + (ar->arWmiReady == TRUE), wmitimeout * HZ);
|
| + if (!timeleft || signal_pending(current)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("ar6000 : Failed to get wmi ready \n"));
|
| + status = A_ERROR;
|
| + break;
|
| + }
|
| +#endif
|
| + status = A_OK;
|
| + } else if (status == A_OK) {
|
| + ar6000_restart_endpoint(ar->arNetDev);
|
| + status = A_OK;
|
| + }
|
| + } else if (state == WLAN_DISABLED) {
|
| +
|
| +
|
| + /* Already in cut power state.. exit */
|
| + if (ar->arWlanPowerState == WLAN_POWER_STATE_CUT_PWR) {
|
| + break;
|
| + }
|
| + ar6000_stop_endpoint(ar->arNetDev, TRUE, FALSE);
|
| +
|
| + config = HIF_DEVICE_POWER_CUT;
|
| + status = HIFConfigureDevice(ar->arHifDevice,
|
| + HIF_DEVICE_POWER_STATE_CHANGE,
|
| + &config,
|
| + sizeof(HIF_DEVICE_POWER_CHANGE_TYPE));
|
| +
|
| + plat_setup_power(0,0);
|
| +
|
| + ar->arWlanPowerState = WLAN_POWER_STATE_CUT_PWR;
|
| + }
|
| + } while (0);
|
| +
|
| + return status;
|
| +}
|
| +
|
| +A_STATUS
|
| +ar6000_setup_deep_sleep_state(struct ar6_softc *ar, AR6000_WLAN_STATE state)
|
| +{
|
| + A_STATUS status = A_OK;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM, ("%s: Deep sleep %d %d \n", __func__,state, ar->arWlanPowerState));
|
| +#ifdef CONFIG_PM
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM, ("Wlan OFF %d BT OFf %d \n", ar->arWlanOff, ar->arBTOff));
|
| +#endif
|
| + do {
|
| + WMI_SET_HOST_SLEEP_MODE_CMD hostSleepMode;
|
| +
|
| + if (state == WLAN_ENABLED) {
|
| + A_UINT16 fg_start_period;
|
| +
|
| + /* Not in deep sleep state.. exit */
|
| + if (ar->arWlanPowerState != WLAN_POWER_STATE_DEEP_SLEEP) {
|
| + if (ar->arWlanPowerState != WLAN_POWER_STATE_ON) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Strange state when we resume from deep sleep %d\n", ar->arWlanPowerState));
|
| + }
|
| + break;
|
| + }
|
| +
|
| + fg_start_period = (ar->scParams.fg_start_period==0) ? 1 : ar->scParams.fg_start_period;
|
| + hostSleepMode.awake = TRUE;
|
| + hostSleepMode.asleep = FALSE;
|
| +
|
| + if ((status=wmi_set_host_sleep_mode_cmd(ar->arWmi, &hostSleepMode)) != A_OK) {
|
| + break;
|
| + }
|
| +
|
| + /* Change the state to ON */
|
| + ar->arWlanPowerState = WLAN_POWER_STATE_ON;
|
| +
|
| + /* Enable foreground scanning */
|
| + if ((status=wmi_scanparams_cmd(ar->arWmi, fg_start_period,
|
| + ar->scParams.fg_end_period,
|
| + ar->scParams.bg_period,
|
| + ar->scParams.minact_chdwell_time,
|
| + ar->scParams.maxact_chdwell_time,
|
| + ar->scParams.pas_chdwell_time,
|
| + ar->scParams.shortScanRatio,
|
| + ar->scParams.scanCtrlFlags,
|
| + ar->scParams.max_dfsch_act_time,
|
| + ar->scParams.maxact_scan_per_ssid)) != A_OK)
|
| + {
|
| + break;
|
| + }
|
| +
|
| + if (ar->arNetworkType != AP_NETWORK)
|
| + {
|
| + if (ar->arSsidLen) {
|
| + if (ar6000_connect_to_ap(ar) != A_OK) {
|
| + /* no need to report error if connection failed */
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + } else if (state == WLAN_DISABLED){
|
| + WMI_SET_WOW_MODE_CMD wowMode = { .enable_wow = FALSE };
|
| +
|
| + /* Already in deep sleep state.. exit */
|
| + if (ar->arWlanPowerState != WLAN_POWER_STATE_ON) {
|
| + if (ar->arWlanPowerState != WLAN_POWER_STATE_DEEP_SLEEP) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Strange state when we suspend for deep sleep %d\n", ar->arWlanPowerState));
|
| + }
|
| + break;
|
| + }
|
| +
|
| + if (ar->arNetworkType != AP_NETWORK)
|
| + {
|
| + /* Disconnect from the AP and disable foreground scanning */
|
| + AR6000_SPIN_LOCK(&ar->arLock, 0);
|
| + if (ar->arConnected == TRUE || ar->arConnectPending == TRUE) {
|
| + AR6000_SPIN_UNLOCK(&ar->arLock, 0);
|
| + wmi_disconnect_cmd(ar->arWmi);
|
| + } else {
|
| + AR6000_SPIN_UNLOCK(&ar->arLock, 0);
|
| + }
|
| + }
|
| +
|
| + ar->scan_triggered = 0;
|
| +
|
| + if ((status=wmi_scanparams_cmd(ar->arWmi, 0xFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0)) != A_OK) {
|
| + break;
|
| + }
|
| +
|
| + /* make sure we disable wow for deep sleep */
|
| + if ((status=wmi_set_wow_mode_cmd(ar->arWmi, &wowMode))!=A_OK)
|
| + {
|
| + break;
|
| + }
|
| +
|
| + ar6000_TxDataCleanup(ar);
|
| +#ifndef ATH6K_CONFIG_OTA_MODE
|
| + wmi_powermode_cmd(ar->arWmi, REC_POWER);
|
| +#endif
|
| +
|
| + hostSleepMode.awake = FALSE;
|
| + hostSleepMode.asleep = TRUE;
|
| + if ((status=wmi_set_host_sleep_mode_cmd(ar->arWmi, &hostSleepMode))!=A_OK) {
|
| + break;
|
| + }
|
| + if (ar->arTxPending[ar->arControlEp]) {
|
| + A_UINT32 timeleft = wait_event_interruptible_timeout(arEvent,
|
| + ar->arTxPending[ar->arControlEp] == 0, wmitimeout * HZ);
|
| + if (!timeleft || signal_pending(current)) {
|
| + status = A_ERROR;
|
| + break;
|
| + }
|
| + }
|
| + status = hifWaitForPendingRecv(ar->arHifDevice);
|
| +
|
| + ar->arWlanPowerState = WLAN_POWER_STATE_DEEP_SLEEP;
|
| + }
|
| + } while (0);
|
| +
|
| + if (status!=A_OK) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Fail to enter/exit deep sleep %d\n", state));
|
| + }
|
| +
|
| + return status;
|
| +}
|
| +
|
| +A_STATUS
|
| +ar6000_update_wlan_pwr_state(struct ar6_softc *ar, AR6000_WLAN_STATE state, A_BOOL pmEvent)
|
| +{
|
| + A_STATUS status = A_OK;
|
| + A_UINT16 powerState, oldPowerState;
|
| + AR6000_WLAN_STATE oldstate = ar->arWlanState;
|
| + A_BOOL wlanOff = ar->arWlanOff;
|
| +#ifdef CONFIG_PM
|
| + A_BOOL btOff = ar->arBTOff;
|
| +#endif /* CONFIG_PM */
|
| +
|
| + if ((state!=WLAN_DISABLED && state!=WLAN_ENABLED)) {
|
| + return A_ERROR;
|
| + }
|
| +
|
| + if (ar->bIsDestroyProgress) {
|
| + return A_EBUSY;
|
| + }
|
| +
|
| + if (down_interruptible(&ar->arSem)) {
|
| + return A_ERROR;
|
| + }
|
| +
|
| + if (ar->bIsDestroyProgress) {
|
| + up(&ar->arSem);
|
| + return A_EBUSY;
|
| + }
|
| +
|
| + ar->arWlanState = wlanOff ? WLAN_DISABLED : state;
|
| + oldPowerState = ar->arWlanPowerState;
|
| + if (state == WLAN_ENABLED) {
|
| + powerState = ar->arWlanPowerState;
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM, ("WLAN PWR set to ENABLE^^\n"));
|
| + if (!wlanOff) {
|
| + if (powerState == WLAN_POWER_STATE_DEEP_SLEEP) {
|
| + status = ar6000_setup_deep_sleep_state(ar, WLAN_ENABLED);
|
| + } else if (powerState == WLAN_POWER_STATE_CUT_PWR) {
|
| + status = ar6000_setup_cut_power_state(ar, WLAN_ENABLED);
|
| + }
|
| + }
|
| +#ifdef CONFIG_PM
|
| + else if (pmEvent && wlanOff) {
|
| + A_BOOL allowCutPwr = ((!ar->arBTSharing) || btOff);
|
| + if ((powerState==WLAN_POWER_STATE_CUT_PWR) && (!allowCutPwr)) {
|
| + /* Come out of cut power */
|
| + ar6000_setup_cut_power_state(ar, WLAN_ENABLED);
|
| + status = ar6000_setup_deep_sleep_state(ar, WLAN_DISABLED);
|
| + }
|
| + }
|
| +#endif /* CONFIG_PM */
|
| + } else if (state == WLAN_DISABLED) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM, ("WLAN PWR set to DISABLED~\n"));
|
| + powerState = WLAN_POWER_STATE_DEEP_SLEEP;
|
| +#ifdef CONFIG_PM
|
| + if (pmEvent) { /* disable due to suspend */
|
| + A_BOOL suspendCutPwr = (ar->arSuspendConfig == WLAN_SUSPEND_CUT_PWR ||
|
| + (ar->arSuspendConfig == WLAN_SUSPEND_WOW &&
|
| + ar->arWow2Config==WLAN_SUSPEND_CUT_PWR));
|
| + A_BOOL suspendCutIfBtOff = ((ar->arSuspendConfig ==
|
| + WLAN_SUSPEND_CUT_PWR_IF_BT_OFF ||
|
| + (ar->arSuspendConfig == WLAN_SUSPEND_WOW &&
|
| + ar->arWow2Config==WLAN_SUSPEND_CUT_PWR_IF_BT_OFF)) &&
|
| + (!ar->arBTSharing || btOff));
|
| + if ((suspendCutPwr) ||
|
| + (suspendCutIfBtOff) ||
|
| + (ar->arWlanState==WLAN_POWER_STATE_CUT_PWR))
|
| + {
|
| + powerState = WLAN_POWER_STATE_CUT_PWR;
|
| + }
|
| + } else {
|
| + if ((wlanOff) &&
|
| + (ar->arWlanOffConfig == WLAN_OFF_CUT_PWR) &&
|
| + (!ar->arBTSharing || btOff))
|
| + {
|
| + /* For BT clock sharing designs, CUT_POWER depend on BT state */
|
| + powerState = WLAN_POWER_STATE_CUT_PWR;
|
| + }
|
| + }
|
| +#endif /* CONFIG_PM */
|
| +
|
| + if (powerState == WLAN_POWER_STATE_DEEP_SLEEP) {
|
| + if (ar->arWlanPowerState == WLAN_POWER_STATE_CUT_PWR) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM, ("Load firmware before set to deep sleep\n"));
|
| + ar6000_setup_cut_power_state(ar, WLAN_ENABLED);
|
| + }
|
| + status = ar6000_setup_deep_sleep_state(ar, WLAN_DISABLED);
|
| + } else if (powerState == WLAN_POWER_STATE_CUT_PWR) {
|
| + status = ar6000_setup_cut_power_state(ar, WLAN_DISABLED);
|
| + }
|
| +
|
| + }
|
| +
|
| + if (status!=A_OK) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Fail to setup WLAN state %d\n", ar->arWlanState));
|
| + ar->arWlanState = oldstate;
|
| + } else if (status == A_OK) {
|
| + WMI_REPORT_SLEEP_STATE_EVENT wmiSleepEvent, *pSleepEvent = NULL;
|
| + if ((ar->arWlanPowerState == WLAN_POWER_STATE_ON) && (oldPowerState != WLAN_POWER_STATE_ON)) {
|
| + wmiSleepEvent.sleepState = WMI_REPORT_SLEEP_STATUS_IS_AWAKE;
|
| + pSleepEvent = &wmiSleepEvent;
|
| + } else if ((ar->arWlanPowerState != WLAN_POWER_STATE_ON) && (oldPowerState == WLAN_POWER_STATE_ON)) {
|
| + wmiSleepEvent.sleepState = WMI_REPORT_SLEEP_STATUS_IS_DEEP_SLEEP;
|
| + pSleepEvent = &wmiSleepEvent;
|
| + }
|
| + if (pSleepEvent) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_PM, ("SENT WLAN Sleep Event %d\n", wmiSleepEvent.sleepState));
|
| + ar6000_send_event_to_app(ar, WMI_REPORT_SLEEP_STATE_EVENTID, (A_UINT8*)pSleepEvent,
|
| + sizeof(WMI_REPORT_SLEEP_STATE_EVENTID));
|
| + }
|
| + }
|
| + up(&ar->arSem);
|
| + return status;
|
| +}
|
| +
|
| +A_STATUS
|
| +ar6000_set_bt_hw_state(struct ar6_softc *ar, A_UINT32 enable)
|
| +{
|
| +#ifdef CONFIG_PM
|
| + A_BOOL off = (enable == 0);
|
| + A_STATUS status;
|
| + if (ar->arBTOff == off) {
|
| + return A_OK;
|
| + }
|
| + ar->arBTOff = off;
|
| + status = ar6000_update_wlan_pwr_state(ar, ar->arWlanOff ? WLAN_DISABLED : WLAN_ENABLED, FALSE);
|
| + return status;
|
| +#else
|
| + return A_OK;
|
| +#endif
|
| +}
|
| +
|
| +A_STATUS
|
| +ar6000_set_wlan_state(struct ar6_softc *ar, AR6000_WLAN_STATE state)
|
| +{
|
| + A_STATUS status;
|
| + A_BOOL off = (state == WLAN_DISABLED);
|
| + if (ar->arWlanOff == off) {
|
| + return A_OK;
|
| + }
|
| + ar->arWlanOff = off;
|
| + status = ar6000_update_wlan_pwr_state(ar, state, FALSE);
|
| + return status;
|
| +}
|
| +
|
| +void ar6000_pm_init()
|
| +{
|
| + A_REGISTER_MODULE_DEBUG_INFO(pm);
|
| +#ifdef CONFIG_PM
|
| +#ifdef CONFIG_HAS_WAKELOCK
|
| + wake_lock_init(&ar6k_suspend_wake_lock, WAKE_LOCK_SUSPEND, "ar6k_suspend");
|
| + wake_lock_init(&ar6k_wow_wake_lock, WAKE_LOCK_SUSPEND, "ar6k_wow");
|
| +#endif
|
| + /*
|
| + * Register ar6000_pm_device into system.
|
| + * We should also add platform_device into the first item of array
|
| + * of devices[] in file arch/xxx/mach-xxx/board-xxxx.c
|
| + */
|
| + if (platform_driver_register(&ar6000_pm_device)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("ar6000: fail to register the power control driver.\n"));
|
| + }
|
| +#endif /* CONFIG_PM */
|
| +}
|
| +
|
| +void ar6000_pm_exit()
|
| +{
|
| +#ifdef CONFIG_PM
|
| + platform_driver_unregister(&ar6000_pm_device);
|
| +#ifdef CONFIG_HAS_WAKELOCK
|
| + wake_lock_destroy(&ar6k_suspend_wake_lock);
|
| + wake_lock_destroy(&ar6k_wow_wake_lock);
|
| +#endif
|
| +#endif /* CONFIG_PM */
|
| +}
|
|
|