]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/staging/rtl8712/rtl871x_pwrctrl.c
Merge branch 'master' into tk71
[mv-sheeva.git] / drivers / staging / rtl8712 / rtl871x_pwrctrl.c
diff --git a/drivers/staging/rtl8712/rtl871x_pwrctrl.c b/drivers/staging/rtl8712/rtl871x_pwrctrl.c
new file mode 100644 (file)
index 0000000..9244c8a
--- /dev/null
@@ -0,0 +1,250 @@
+/******************************************************************************
+ * rtl871x_pwrctrl.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License 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 Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL871X_PWRCTRL_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "osdep_intf.h"
+
+#define RTL8712_SDIO_LOCAL_BASE 0X10100000
+#define SDIO_HCPWM (RTL8712_SDIO_LOCAL_BASE + 0x0081)
+
+void r8712_set_rpwm(struct _adapter *padapter, u8 val8)
+{
+       u8      rpwm;
+       struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+       if (pwrpriv->rpwm == val8) {
+               if (pwrpriv->rpwm_retry == 0)
+                       return;
+       }
+       if ((padapter->bDriverStopped == true) ||
+           (padapter->bSurpriseRemoved == true))
+               return;
+       rpwm = val8 | pwrpriv->tog;
+       switch (val8) {
+       case PS_STATE_S1:
+               pwrpriv->cpwm = val8;
+               break;
+       case PS_STATE_S2:/* only for USB normal powersave mode use,
+                         * temp mark some code. */
+       case PS_STATE_S3:
+       case PS_STATE_S4:
+               pwrpriv->cpwm = val8;
+               break;
+       default:
+               break;
+       }
+       pwrpriv->rpwm_retry = 0;
+       pwrpriv->rpwm = val8;
+       r8712_write8(padapter, 0x1025FE58, rpwm);
+       pwrpriv->tog += 0x80;
+}
+
+void r8712_set_ps_mode(struct _adapter *padapter, uint ps_mode, uint smart_ps)
+{
+       struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+       if (ps_mode > PM_Card_Disable)
+               return;
+       /* if driver is in active state, we dont need set smart_ps.*/
+       if (ps_mode == PS_MODE_ACTIVE)
+               smart_ps = 0;
+       if ((pwrpriv->pwr_mode != ps_mode) || (pwrpriv->smart_ps != smart_ps)) {
+               if (pwrpriv->pwr_mode == PS_MODE_ACTIVE)
+                       pwrpriv->bSleep = true;
+               else
+                       pwrpriv->bSleep = false;
+               pwrpriv->pwr_mode = ps_mode;
+               pwrpriv->smart_ps = smart_ps;
+               _set_workitem(&(pwrpriv->SetPSModeWorkItem));
+       }
+}
+
+/*
+ * Caller:ISR handler...
+ *
+ * This will be called when CPWM interrupt is up.
+ *
+ * using to update cpwn of drv; and drv willl make a decision to up or
+ * down pwr level
+ */
+void r8712_cpwm_int_hdl(struct _adapter *padapter,
+                       struct reportpwrstate_parm *preportpwrstate)
+{
+       struct pwrctrl_priv *pwrpriv = &(padapter->pwrctrlpriv);
+       struct cmd_priv *pcmdpriv = &(padapter->cmdpriv);
+       struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+
+       if (pwrpriv->cpwm_tog == ((preportpwrstate->state) & 0x80))
+               return;
+       _cancel_timer_ex(&padapter->pwrctrlpriv. rpwm_check_timer);
+       _enter_pwrlock(&pwrpriv->lock);
+       pwrpriv->cpwm = (preportpwrstate->state) & 0xf;
+       if (pwrpriv->cpwm >= PS_STATE_S2) {
+               if (pwrpriv->alives & CMD_ALIVE)
+                       up(&(pcmdpriv->cmd_queue_sema));
+               if (pwrpriv->alives & XMIT_ALIVE)
+                       up(&(pxmitpriv->xmit_sema));
+       }
+       pwrpriv->cpwm_tog = (preportpwrstate->state) & 0x80;
+       up(&pwrpriv->lock);
+}
+
+static inline void register_task_alive(struct pwrctrl_priv *pwrctrl, uint tag)
+{
+               pwrctrl->alives |= tag;
+}
+
+static inline void unregister_task_alive(struct pwrctrl_priv *pwrctrl, uint tag)
+{
+       if (pwrctrl->alives & tag)
+               pwrctrl->alives ^= tag;
+}
+
+static void _rpwm_check_handler (struct _adapter *padapter)
+{
+       struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+       if (padapter->bDriverStopped == true ||
+           padapter->bSurpriseRemoved == true)
+               return;
+       if (pwrpriv->cpwm != pwrpriv->rpwm)
+               _set_workitem(&(pwrpriv->rpwm_workitem));
+}
+
+static void SetPSModeWorkItemCallback(struct work_struct *work)
+{
+       struct pwrctrl_priv *pwrpriv = container_of(work,
+                                      struct pwrctrl_priv, SetPSModeWorkItem);
+       struct _adapter *padapter = container_of(pwrpriv,
+                                   struct _adapter, pwrctrlpriv);
+       _enter_pwrlock(&pwrpriv->lock);
+       if (!pwrpriv->bSleep) {
+               if (pwrpriv->pwr_mode == PS_MODE_ACTIVE)
+                       r8712_set_rpwm(padapter, PS_STATE_S4);
+       }
+       up(&pwrpriv->lock);
+}
+
+static void rpwm_workitem_callback(struct work_struct *work)
+{
+       struct pwrctrl_priv *pwrpriv = container_of(work,
+                                      struct pwrctrl_priv, rpwm_workitem);
+       struct _adapter *padapter = container_of(pwrpriv,
+                                   struct _adapter, pwrctrlpriv);
+       u8 cpwm = pwrpriv->cpwm;
+       _enter_pwrlock(&pwrpriv->lock);
+       if (pwrpriv->cpwm != pwrpriv->rpwm) {
+               cpwm = r8712_read8(padapter, SDIO_HCPWM);
+               pwrpriv->rpwm_retry = 1;
+               r8712_set_rpwm(padapter, pwrpriv->rpwm);
+       }
+       up(&pwrpriv->lock);
+}
+
+static void rpwm_check_handler (void *FunctionContext)
+{
+       struct _adapter *adapter = (struct _adapter *)FunctionContext;
+       _rpwm_check_handler(adapter);
+}
+
+void r8712_init_pwrctrl_priv(struct _adapter *padapter)
+{
+       struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv;
+
+       memset((unsigned char *)pwrctrlpriv, 0, sizeof(struct pwrctrl_priv));
+       sema_init(&pwrctrlpriv->lock, 1);
+       pwrctrlpriv->cpwm = PS_STATE_S4;
+       pwrctrlpriv->pwr_mode = PS_MODE_ACTIVE;
+       pwrctrlpriv->smart_ps = 0;
+       pwrctrlpriv->tog = 0x80;
+/* clear RPWM to ensure driver and fw back to initial state. */
+       r8712_write8(padapter, 0x1025FE58, 0);
+       _init_workitem(&(pwrctrlpriv->SetPSModeWorkItem),
+                      SetPSModeWorkItemCallback, padapter);
+       _init_workitem(&(pwrctrlpriv->rpwm_workitem),
+                      rpwm_workitem_callback, padapter);
+       _init_timer(&(pwrctrlpriv->rpwm_check_timer),
+                   padapter->pnetdev, rpwm_check_handler, (u8 *)padapter);
+}
+
+/*
+Caller: r8712_cmd_thread
+
+Check if the fw_pwrstate is okay for issuing cmd.
+If not (cpwm should be is less than P2 state), then the sub-routine
+will raise the cpwm to be greater than or equal to P2.
+
+Calling Context: Passive
+
+Return Value:
+
+_SUCCESS: r8712_cmd_thread can issue cmds to firmware afterwards.
+_FAIL: r8712_cmd_thread can not do anything.
+*/
+sint r8712_register_cmd_alive(struct _adapter *padapter)
+{
+       uint res = _SUCCESS;
+       struct pwrctrl_priv *pwrctrl = &padapter->pwrctrlpriv;
+
+       _enter_pwrlock(&pwrctrl->lock);
+       register_task_alive(pwrctrl, CMD_ALIVE);
+       if (pwrctrl->cpwm < PS_STATE_S2) {
+               r8712_set_rpwm(padapter, PS_STATE_S3);
+               res = _FAIL;
+       }
+       up(&pwrctrl->lock);
+       return res;
+}
+
+/*
+Caller: ISR
+
+If ISR's txdone,
+No more pkts for TX,
+Then driver shall call this fun. to power down firmware again.
+*/
+
+void r8712_unregister_cmd_alive(struct _adapter *padapter)
+{
+       struct pwrctrl_priv *pwrctrl = &padapter->pwrctrlpriv;
+
+       _enter_pwrlock(&pwrctrl->lock);
+       unregister_task_alive(pwrctrl, CMD_ALIVE);
+       if ((pwrctrl->cpwm > PS_STATE_S2) &&
+          (pwrctrl->pwr_mode > PS_MODE_ACTIVE)) {
+               if ((pwrctrl->alives == 0) &&
+                   (check_fwstate(&padapter->mlmepriv,
+                    _FW_UNDER_LINKING) != true)) {
+                       r8712_set_rpwm(padapter, PS_STATE_S0);
+               }
+       }
+       up(&pwrctrl->lock);
+}