1e24c1f86SMichael Straube // SPDX-License-Identifier: GPL-2.0
22865d42cSLarry Finger /******************************************************************************
32865d42cSLarry Finger  * rtl871x_pwrctrl.c
42865d42cSLarry Finger  *
52865d42cSLarry Finger  * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
62865d42cSLarry Finger  * Linux device driver for RTL8192SU
72865d42cSLarry Finger  *
82865d42cSLarry Finger  * Modifications for inclusion into the Linux staging tree are
92865d42cSLarry Finger  * Copyright(c) 2010 Larry Finger. All rights reserved.
102865d42cSLarry Finger  *
112865d42cSLarry Finger  * Contact information:
122865d42cSLarry Finger  * WLAN FAE <wlanfae@realtek.com>
132865d42cSLarry Finger  * Larry Finger <Larry.Finger@lwfinger.net>
142865d42cSLarry Finger  *
152865d42cSLarry Finger  ******************************************************************************/
162865d42cSLarry Finger 
172865d42cSLarry Finger #define _RTL871X_PWRCTRL_C_
182865d42cSLarry Finger 
192865d42cSLarry Finger #include "osdep_service.h"
202865d42cSLarry Finger #include "drv_types.h"
212865d42cSLarry Finger #include "osdep_intf.h"
222865d42cSLarry Finger 
232865d42cSLarry Finger #define RTL8712_SDIO_LOCAL_BASE 0X10100000
242865d42cSLarry Finger #define SDIO_HCPWM (RTL8712_SDIO_LOCAL_BASE + 0x0081)
252865d42cSLarry Finger 
r8712_set_rpwm(struct _adapter * padapter,u8 val8)262865d42cSLarry Finger void r8712_set_rpwm(struct _adapter *padapter, u8 val8)
272865d42cSLarry Finger {
282865d42cSLarry Finger 	u8	rpwm;
292865d42cSLarry Finger 	struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
302865d42cSLarry Finger 
312865d42cSLarry Finger 	if (pwrpriv->rpwm == val8) {
322865d42cSLarry Finger 		if (pwrpriv->rpwm_retry == 0)
332865d42cSLarry Finger 			return;
342865d42cSLarry Finger 	}
355b8d98f1SHimadri Pandya 	if (padapter->driver_stopped || padapter->surprise_removed)
362865d42cSLarry Finger 		return;
372865d42cSLarry Finger 	rpwm = val8 | pwrpriv->tog;
382865d42cSLarry Finger 	switch (val8) {
392865d42cSLarry Finger 	case PS_STATE_S1:
402865d42cSLarry Finger 		pwrpriv->cpwm = val8;
412865d42cSLarry Finger 		break;
422865d42cSLarry Finger 	case PS_STATE_S2:/* only for USB normal powersave mode use,
43bef611a9SRaphaël Beamonte 			  * temp mark some code.
44bef611a9SRaphaël Beamonte 			  */
452865d42cSLarry Finger 	case PS_STATE_S3:
462865d42cSLarry Finger 	case PS_STATE_S4:
472865d42cSLarry Finger 		pwrpriv->cpwm = val8;
482865d42cSLarry Finger 		break;
492865d42cSLarry Finger 	default:
502865d42cSLarry Finger 		break;
512865d42cSLarry Finger 	}
522865d42cSLarry Finger 	pwrpriv->rpwm_retry = 0;
532865d42cSLarry Finger 	pwrpriv->rpwm = val8;
542865d42cSLarry Finger 	r8712_write8(padapter, 0x1025FE58, rpwm);
552865d42cSLarry Finger 	pwrpriv->tog += 0x80;
562865d42cSLarry Finger }
572865d42cSLarry Finger 
r8712_set_ps_mode(struct _adapter * padapter,uint ps_mode,uint smart_ps)582865d42cSLarry Finger void r8712_set_ps_mode(struct _adapter *padapter, uint ps_mode, uint smart_ps)
592865d42cSLarry Finger {
602865d42cSLarry Finger 	struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
612865d42cSLarry Finger 
622865d42cSLarry Finger 	if (ps_mode > PM_Card_Disable)
632865d42cSLarry Finger 		return;
642865d42cSLarry Finger 	/* if driver is in active state, we dont need set smart_ps.*/
652865d42cSLarry Finger 	if (ps_mode == PS_MODE_ACTIVE)
662865d42cSLarry Finger 		smart_ps = 0;
672865d42cSLarry Finger 	if ((pwrpriv->pwr_mode != ps_mode) || (pwrpriv->smart_ps != smart_ps)) {
682865d42cSLarry Finger 		if (pwrpriv->pwr_mode == PS_MODE_ACTIVE)
692865d42cSLarry Finger 			pwrpriv->bSleep = true;
702865d42cSLarry Finger 		else
712865d42cSLarry Finger 			pwrpriv->bSleep = false;
722865d42cSLarry Finger 		pwrpriv->pwr_mode = ps_mode;
732865d42cSLarry Finger 		pwrpriv->smart_ps = smart_ps;
74f2355b69SJames A Shackleford 		schedule_work(&pwrpriv->SetPSModeWorkItem);
752865d42cSLarry Finger 	}
762865d42cSLarry Finger }
772865d42cSLarry Finger 
782865d42cSLarry Finger /*
792865d42cSLarry Finger  * Caller:ISR handler...
802865d42cSLarry Finger  *
812865d42cSLarry Finger  * This will be called when CPWM interrupt is up.
822865d42cSLarry Finger  *
833cbeb42eSJustin P. Mattock  * using to update cpwn of drv; and drv will make a decision to up or
842865d42cSLarry Finger  * down pwr level
852865d42cSLarry Finger  */
r8712_cpwm_int_hdl(struct _adapter * padapter,struct reportpwrstate_parm * preportpwrstate)862865d42cSLarry Finger void r8712_cpwm_int_hdl(struct _adapter *padapter,
872865d42cSLarry Finger 			struct reportpwrstate_parm *preportpwrstate)
882865d42cSLarry Finger {
892865d42cSLarry Finger 	struct pwrctrl_priv *pwrpriv = &(padapter->pwrctrlpriv);
902865d42cSLarry Finger 	struct cmd_priv	*pcmdpriv = &(padapter->cmdpriv);
912865d42cSLarry Finger 
922865d42cSLarry Finger 	if (pwrpriv->cpwm_tog == ((preportpwrstate->state) & 0x80))
932865d42cSLarry Finger 		return;
9439a6e737SSudip Mukherjee 	del_timer(&padapter->pwrctrlpriv.rpwm_check_timer);
955c2ba8b8SBinoy Jayan 	mutex_lock(&pwrpriv->mutex_lock);
962865d42cSLarry Finger 	pwrpriv->cpwm = (preportpwrstate->state) & 0xf;
972865d42cSLarry Finger 	if (pwrpriv->cpwm >= PS_STATE_S2) {
982865d42cSLarry Finger 		if (pwrpriv->alives & CMD_ALIVE)
990f89054aSBinoy Jayan 			complete(&(pcmdpriv->cmd_queue_comp));
1002865d42cSLarry Finger 	}
1012865d42cSLarry Finger 	pwrpriv->cpwm_tog = (preportpwrstate->state) & 0x80;
1025c2ba8b8SBinoy Jayan 	mutex_unlock(&pwrpriv->mutex_lock);
1032865d42cSLarry Finger }
1042865d42cSLarry Finger 
register_task_alive(struct pwrctrl_priv * pwrctrl,uint tag)1052865d42cSLarry Finger static inline void register_task_alive(struct pwrctrl_priv *pwrctrl, uint tag)
1062865d42cSLarry Finger {
1072865d42cSLarry Finger 		pwrctrl->alives |= tag;
1082865d42cSLarry Finger }
1092865d42cSLarry Finger 
unregister_task_alive(struct pwrctrl_priv * pwrctrl,uint tag)1102865d42cSLarry Finger static inline void unregister_task_alive(struct pwrctrl_priv *pwrctrl, uint tag)
1112865d42cSLarry Finger {
1122865d42cSLarry Finger 	if (pwrctrl->alives & tag)
1132865d42cSLarry Finger 		pwrctrl->alives ^= tag;
1142865d42cSLarry Finger }
1152865d42cSLarry Finger 
_rpwm_check_handler(struct _adapter * padapter)1162865d42cSLarry Finger static void _rpwm_check_handler (struct _adapter *padapter)
1172865d42cSLarry Finger {
1182865d42cSLarry Finger 	struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
1192865d42cSLarry Finger 
1205b8d98f1SHimadri Pandya 	if (padapter->driver_stopped || padapter->surprise_removed)
1212865d42cSLarry Finger 		return;
1222865d42cSLarry Finger 	if (pwrpriv->cpwm != pwrpriv->rpwm)
123f2355b69SJames A Shackleford 		schedule_work(&pwrpriv->rpwm_workitem);
1242865d42cSLarry Finger }
1252865d42cSLarry Finger 
SetPSModeWorkItemCallback(struct work_struct * work)1262865d42cSLarry Finger static void SetPSModeWorkItemCallback(struct work_struct *work)
1272865d42cSLarry Finger {
1282865d42cSLarry Finger 	struct pwrctrl_priv *pwrpriv = container_of(work,
1292865d42cSLarry Finger 				       struct pwrctrl_priv, SetPSModeWorkItem);
1302865d42cSLarry Finger 	struct _adapter *padapter = container_of(pwrpriv,
1312865d42cSLarry Finger 				    struct _adapter, pwrctrlpriv);
1322865d42cSLarry Finger 	if (!pwrpriv->bSleep) {
1335c2ba8b8SBinoy Jayan 		mutex_lock(&pwrpriv->mutex_lock);
1342865d42cSLarry Finger 		if (pwrpriv->pwr_mode == PS_MODE_ACTIVE)
1352865d42cSLarry Finger 			r8712_set_rpwm(padapter, PS_STATE_S4);
1364db7c0beSWei Yongjun 		mutex_unlock(&pwrpriv->mutex_lock);
1372865d42cSLarry Finger 	}
13886587b67SLarry Finger }
1392865d42cSLarry Finger 
rpwm_workitem_callback(struct work_struct * work)1402865d42cSLarry Finger static void rpwm_workitem_callback(struct work_struct *work)
1412865d42cSLarry Finger {
1422865d42cSLarry Finger 	struct pwrctrl_priv *pwrpriv = container_of(work,
1432865d42cSLarry Finger 				       struct pwrctrl_priv, rpwm_workitem);
1442865d42cSLarry Finger 	struct _adapter *padapter = container_of(pwrpriv,
1452865d42cSLarry Finger 				    struct _adapter, pwrctrlpriv);
1462865d42cSLarry Finger 	if (pwrpriv->cpwm != pwrpriv->rpwm) {
1475c2ba8b8SBinoy Jayan 		mutex_lock(&pwrpriv->mutex_lock);
148e29d3ebcSSudip Mukherjee 		r8712_read8(padapter, SDIO_HCPWM);
1492865d42cSLarry Finger 		pwrpriv->rpwm_retry = 1;
1502865d42cSLarry Finger 		r8712_set_rpwm(padapter, pwrpriv->rpwm);
1515c2ba8b8SBinoy Jayan 		mutex_unlock(&pwrpriv->mutex_lock);
1522865d42cSLarry Finger 	}
15386587b67SLarry Finger }
1542865d42cSLarry Finger 
rpwm_check_handler(struct timer_list * t)15536aeebd4SKees Cook static void rpwm_check_handler (struct timer_list *t)
1562865d42cSLarry Finger {
15736aeebd4SKees Cook 	struct _adapter *adapter =
15836aeebd4SKees Cook 		from_timer(adapter, t, pwrctrlpriv.rpwm_check_timer);
15902a29d2dSTapasweni Pathak 
1602865d42cSLarry Finger 	_rpwm_check_handler(adapter);
1612865d42cSLarry Finger }
1622865d42cSLarry Finger 
r8712_init_pwrctrl_priv(struct _adapter * padapter)1632865d42cSLarry Finger void r8712_init_pwrctrl_priv(struct _adapter *padapter)
1642865d42cSLarry Finger {
1652865d42cSLarry Finger 	struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv;
1662865d42cSLarry Finger 
1672865d42cSLarry Finger 	memset((unsigned char *)pwrctrlpriv, 0, sizeof(struct pwrctrl_priv));
1685c2ba8b8SBinoy Jayan 	mutex_init(&pwrctrlpriv->mutex_lock);
1692865d42cSLarry Finger 	pwrctrlpriv->cpwm = PS_STATE_S4;
1702865d42cSLarry Finger 	pwrctrlpriv->pwr_mode = PS_MODE_ACTIVE;
1712865d42cSLarry Finger 	pwrctrlpriv->smart_ps = 0;
1722865d42cSLarry Finger 	pwrctrlpriv->tog = 0x80;
1732865d42cSLarry Finger /* clear RPWM to ensure driver and fw back to initial state. */
1742865d42cSLarry Finger 	r8712_write8(padapter, 0x1025FE58, 0);
1759f952489SJames A Shackleford 	INIT_WORK(&pwrctrlpriv->SetPSModeWorkItem, SetPSModeWorkItemCallback);
1769f952489SJames A Shackleford 	INIT_WORK(&pwrctrlpriv->rpwm_workitem, rpwm_workitem_callback);
17736aeebd4SKees Cook 	timer_setup(&pwrctrlpriv->rpwm_check_timer, rpwm_check_handler, 0);
1782865d42cSLarry Finger }
1792865d42cSLarry Finger 
1802865d42cSLarry Finger /*
18109b080f7SVijai Kumar K  * Caller: r8712_cmd_thread
18209b080f7SVijai Kumar K  * Check if the fw_pwrstate is okay for issuing cmd.
18309b080f7SVijai Kumar K  * If not (cpwm should be is less than P2 state), then the sub-routine
18409b080f7SVijai Kumar K  * will raise the cpwm to be greater than or equal to P2.
18509b080f7SVijai Kumar K  * Calling Context: Passive
18609b080f7SVijai Kumar K  * Return Value:
1877c93fdf0SNishka Dasgupta  * 0:	    r8712_cmd_thread can issue cmds to firmware afterwards.
1887c93fdf0SNishka Dasgupta  * -EINVAL: r8712_cmd_thread can not do anything.
1892865d42cSLarry Finger  */
r8712_register_cmd_alive(struct _adapter * padapter)1907c93fdf0SNishka Dasgupta int r8712_register_cmd_alive(struct _adapter *padapter)
1912865d42cSLarry Finger {
1927c93fdf0SNishka Dasgupta 	int res = 0;
1932865d42cSLarry Finger 	struct pwrctrl_priv *pwrctrl = &padapter->pwrctrlpriv;
1942865d42cSLarry Finger 
1955c2ba8b8SBinoy Jayan 	mutex_lock(&pwrctrl->mutex_lock);
1962865d42cSLarry Finger 	register_task_alive(pwrctrl, CMD_ALIVE);
1972865d42cSLarry Finger 	if (pwrctrl->cpwm < PS_STATE_S2) {
1982865d42cSLarry Finger 		r8712_set_rpwm(padapter, PS_STATE_S3);
1997c93fdf0SNishka Dasgupta 		res = -EINVAL;
2002865d42cSLarry Finger 	}
2015c2ba8b8SBinoy Jayan 	mutex_unlock(&pwrctrl->mutex_lock);
2022865d42cSLarry Finger 	return res;
2032865d42cSLarry Finger }
2042865d42cSLarry Finger 
2052865d42cSLarry Finger /*
20609b080f7SVijai Kumar K  * Caller: ISR
20709b080f7SVijai Kumar K  * If ISR's txdone,
20809b080f7SVijai Kumar K  * No more pkts for TX,
20909b080f7SVijai Kumar K  * Then driver shall call this fun. to power down firmware again.
2102865d42cSLarry Finger  */
r8712_unregister_cmd_alive(struct _adapter * padapter)2112865d42cSLarry Finger void r8712_unregister_cmd_alive(struct _adapter *padapter)
2122865d42cSLarry Finger {
2132865d42cSLarry Finger 	struct pwrctrl_priv *pwrctrl = &padapter->pwrctrlpriv;
2142865d42cSLarry Finger 
2155c2ba8b8SBinoy Jayan 	mutex_lock(&pwrctrl->mutex_lock);
2162865d42cSLarry Finger 	unregister_task_alive(pwrctrl, CMD_ALIVE);
2172865d42cSLarry Finger 	if ((pwrctrl->cpwm > PS_STATE_S2) &&
2182865d42cSLarry Finger 	   (pwrctrl->pwr_mode > PS_MODE_ACTIVE)) {
2192865d42cSLarry Finger 		if ((pwrctrl->alives == 0) &&
2202865d42cSLarry Finger 		    (check_fwstate(&padapter->mlmepriv,
2212865d42cSLarry Finger 		     _FW_UNDER_LINKING) != true)) {
2222865d42cSLarry Finger 			r8712_set_rpwm(padapter, PS_STATE_S0);
2232865d42cSLarry Finger 		}
2242865d42cSLarry Finger 	}
2255c2ba8b8SBinoy Jayan 	mutex_unlock(&pwrctrl->mutex_lock);
2262865d42cSLarry Finger }
227*9be550eeSPavel Skripkin 
r8712_flush_rwctrl_works(struct _adapter * padapter)228*9be550eeSPavel Skripkin void r8712_flush_rwctrl_works(struct _adapter *padapter)
229*9be550eeSPavel Skripkin {
230*9be550eeSPavel Skripkin 	struct pwrctrl_priv *pwrctrl = &padapter->pwrctrlpriv;
231*9be550eeSPavel Skripkin 
232*9be550eeSPavel Skripkin 	flush_work(&pwrctrl->SetPSModeWorkItem);
233*9be550eeSPavel Skripkin 	flush_work(&pwrctrl->rpwm_workitem);
234*9be550eeSPavel Skripkin }
235