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