2009-04-29 23:33:31 +03:00
/*
2009-06-12 14:17:39 +03:00
* This file is part of wl1251
2009-04-29 23:33:31 +03:00
*
* Copyright ( C ) 2008 Nokia Corporation
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*
*/
2010-10-10 11:28:32 +03:00
# include "reg.h"
# include "ps.h"
# include "cmd.h"
# include "io.h"
2009-04-29 23:33:31 +03:00
2009-11-30 10:17:59 +02:00
/* in ms */
# define WL1251_WAKEUP_TIMEOUT 100
2009-04-29 23:33:31 +03:00
2009-11-17 18:48:37 +02:00
void wl1251_elp_work ( struct work_struct * work )
2009-04-29 23:33:31 +03:00
{
2009-11-17 18:48:37 +02:00
struct delayed_work * dwork ;
struct wl1251 * wl ;
dwork = container_of ( work , struct delayed_work , work ) ;
wl = container_of ( dwork , struct wl1251 , elp_work ) ;
wl1251_debug ( DEBUG_PSM , " elp work " ) ;
mutex_lock ( & wl - > mutex ) ;
2011-04-04 11:04:57 +03:00
if ( wl - > elp | | wl - > station_mode = = STATION_ACTIVE_MODE )
2009-11-17 18:48:37 +02:00
goto out ;
2009-04-29 23:33:31 +03:00
2009-06-12 14:17:39 +03:00
wl1251_debug ( DEBUG_PSM , " chip to elp " ) ;
2010-03-11 17:44:57 +02:00
wl1251_write_elp ( wl , HW_ACCESS_ELP_CTRL_REG_ADDR , ELPCTRL_SLEEP ) ;
2009-04-29 23:33:31 +03:00
wl - > elp = true ;
2009-11-17 18:48:37 +02:00
out :
mutex_unlock ( & wl - > mutex ) ;
}
# define ELP_ENTRY_DELAY 5
/* Routines to toggle sleep mode while in ELP */
void wl1251_ps_elp_sleep ( struct wl1251 * wl )
{
unsigned long delay ;
2011-04-04 11:04:57 +03:00
if ( wl - > station_mode ! = STATION_ACTIVE_MODE ) {
2009-11-17 18:48:37 +02:00
delay = msecs_to_jiffies ( ELP_ENTRY_DELAY ) ;
ieee80211_queue_delayed_work ( wl - > hw , & wl - > elp_work , delay ) ;
}
2009-04-29 23:33:31 +03:00
}
2009-06-12 14:17:39 +03:00
int wl1251_ps_elp_wakeup ( struct wl1251 * wl )
2009-04-29 23:33:31 +03:00
{
2009-11-30 10:18:06 +02:00
unsigned long timeout , start ;
2009-04-29 23:33:31 +03:00
u32 elp_reg ;
2012-12-21 17:56:59 -08:00
cancel_delayed_work ( & wl - > elp_work ) ;
2011-03-06 19:23:37 +02:00
2009-04-29 23:33:31 +03:00
if ( ! wl - > elp )
return 0 ;
2009-06-12 14:17:39 +03:00
wl1251_debug ( DEBUG_PSM , " waking up chip from elp " ) ;
2009-04-29 23:33:31 +03:00
2009-11-30 10:18:06 +02:00
start = jiffies ;
2009-06-12 14:17:39 +03:00
timeout = jiffies + msecs_to_jiffies ( WL1251_WAKEUP_TIMEOUT ) ;
2009-04-29 23:33:31 +03:00
2010-03-11 17:44:57 +02:00
wl1251_write_elp ( wl , HW_ACCESS_ELP_CTRL_REG_ADDR , ELPCTRL_WAKE_UP ) ;
2009-04-29 23:33:31 +03:00
2010-03-11 17:44:57 +02:00
elp_reg = wl1251_read_elp ( wl , HW_ACCESS_ELP_CTRL_REG_ADDR ) ;
2009-04-29 23:33:31 +03:00
/*
* FIXME : we should wait for irq from chip but , as a temporary
* solution to simplify locking , let ' s poll instead
*/
while ( ! ( elp_reg & ELPCTRL_WLAN_READY ) ) {
if ( time_after ( jiffies , timeout ) ) {
2009-06-12 14:17:39 +03:00
wl1251_error ( " elp wakeup timeout " ) ;
2009-04-29 23:33:31 +03:00
return - ETIMEDOUT ;
}
msleep ( 1 ) ;
2010-03-11 17:44:57 +02:00
elp_reg = wl1251_read_elp ( wl , HW_ACCESS_ELP_CTRL_REG_ADDR ) ;
2009-04-29 23:33:31 +03:00
}
2009-06-12 14:17:39 +03:00
wl1251_debug ( DEBUG_PSM , " wakeup time: %u ms " ,
2009-11-30 10:18:06 +02:00
jiffies_to_msecs ( jiffies - start ) ) ;
2009-04-29 23:33:31 +03:00
wl - > elp = false ;
return 0 ;
}
2011-04-04 11:04:57 +03:00
int wl1251_ps_set_mode ( struct wl1251 * wl , enum wl1251_station_mode mode )
2009-04-29 23:33:31 +03:00
{
int ret ;
switch ( mode ) {
case STATION_POWER_SAVE_MODE :
2009-06-12 14:17:39 +03:00
wl1251_debug ( DEBUG_PSM , " entering psm " ) ;
2009-08-07 13:34:56 +03:00
2009-11-17 18:48:30 +02:00
/* enable beacon filtering */
ret = wl1251_acx_beacon_filter_opt ( wl , true ) ;
if ( ret < 0 )
return ret ;
2009-08-07 13:34:56 +03:00
ret = wl1251_acx_wake_up_conditions ( wl ,
WAKE_UP_EVENT_DTIM_BITMAP ,
wl - > listen_int ) ;
if ( ret < 0 )
return ret ;
2011-01-30 20:10:57 +01:00
ret = wl1251_acx_bet_enable ( wl , WL1251_ACX_BET_ENABLE ,
WL1251_DEFAULT_BET_CONSECUTIVE ) ;
if ( ret < 0 )
return ret ;
2011-04-04 11:04:57 +03:00
ret = wl1251_cmd_ps_mode ( wl , CHIP_POWER_SAVE_MODE ) ;
2009-04-29 23:33:31 +03:00
if ( ret < 0 )
return ret ;
2011-03-06 19:23:36 +02:00
ret = wl1251_acx_sleep_auth ( wl , WL1251_PSM_ELP ) ;
2009-04-29 23:33:31 +03:00
if ( ret < 0 )
return ret ;
break ;
2011-04-04 11:04:58 +03:00
case STATION_IDLE :
wl1251_debug ( DEBUG_PSM , " entering idle " ) ;
ret = wl1251_acx_sleep_auth ( wl , WL1251_PSM_ELP ) ;
if ( ret < 0 )
return ret ;
ret = wl1251_cmd_template_set ( wl , CMD_DISCONNECT , NULL , 0 ) ;
if ( ret < 0 )
return ret ;
break ;
2009-04-29 23:33:31 +03:00
case STATION_ACTIVE_MODE :
default :
2009-06-12 14:17:39 +03:00
wl1251_debug ( DEBUG_PSM , " leaving psm " ) ;
2011-03-06 19:23:36 +02:00
ret = wl1251_acx_sleep_auth ( wl , WL1251_PSM_CAM ) ;
2009-04-29 23:33:31 +03:00
if ( ret < 0 )
return ret ;
2011-01-30 20:10:57 +01:00
/* disable BET */
ret = wl1251_acx_bet_enable ( wl , WL1251_ACX_BET_DISABLE ,
WL1251_DEFAULT_BET_CONSECUTIVE ) ;
if ( ret < 0 )
return ret ;
2009-11-17 18:48:30 +02:00
/* disable beacon filtering */
ret = wl1251_acx_beacon_filter_opt ( wl , false ) ;
if ( ret < 0 )
return ret ;
2009-08-07 13:34:56 +03:00
ret = wl1251_acx_wake_up_conditions ( wl ,
WAKE_UP_EVENT_DTIM_BITMAP ,
wl - > listen_int ) ;
if ( ret < 0 )
return ret ;
2011-04-04 11:04:57 +03:00
ret = wl1251_cmd_ps_mode ( wl , CHIP_ACTIVE_MODE ) ;
2009-04-29 23:33:31 +03:00
if ( ret < 0 )
return ret ;
break ;
}
2011-04-04 11:04:57 +03:00
wl - > station_mode = mode ;
2009-04-29 23:33:31 +03:00
return ret ;
}