Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rtl871x_pwrctrl.c
Go to the documentation of this file.
1 /******************************************************************************
2  * rtl871x_pwrctrl.c
3  *
4  * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
5  * Linux device driver for RTL8192SU
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of version 2 of the GNU General Public License as
9  * published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  * Modifications for inclusion into the Linux staging tree are
21  * Copyright(c) 2010 Larry Finger. All rights reserved.
22  *
23  * Contact information:
24  * WLAN FAE <[email protected]>
25  * Larry Finger <[email protected]>
26  *
27  ******************************************************************************/
28 
29 #define _RTL871X_PWRCTRL_C_
30 
31 #include "osdep_service.h"
32 #include "drv_types.h"
33 #include "osdep_intf.h"
34 
35 #define RTL8712_SDIO_LOCAL_BASE 0X10100000
36 #define SDIO_HCPWM (RTL8712_SDIO_LOCAL_BASE + 0x0081)
37 
38 void r8712_set_rpwm(struct _adapter *padapter, u8 val8)
39 {
40  u8 rpwm;
41  struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
42 
43  if (pwrpriv->rpwm == val8) {
44  if (pwrpriv->rpwm_retry == 0)
45  return;
46  }
47  if ((padapter->bDriverStopped == true) ||
48  (padapter->bSurpriseRemoved == true))
49  return;
50  rpwm = val8 | pwrpriv->tog;
51  switch (val8) {
52  case PS_STATE_S1:
53  pwrpriv->cpwm = val8;
54  break;
55  case PS_STATE_S2:/* only for USB normal powersave mode use,
56  * temp mark some code. */
57  case PS_STATE_S3:
58  case PS_STATE_S4:
59  pwrpriv->cpwm = val8;
60  break;
61  default:
62  break;
63  }
64  pwrpriv->rpwm_retry = 0;
65  pwrpriv->rpwm = val8;
66  r8712_write8(padapter, 0x1025FE58, rpwm);
67  pwrpriv->tog += 0x80;
68 }
69 
70 void r8712_set_ps_mode(struct _adapter *padapter, uint ps_mode, uint smart_ps)
71 {
72  struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
73 
74  if (ps_mode > PM_Card_Disable)
75  return;
76  /* if driver is in active state, we dont need set smart_ps.*/
77  if (ps_mode == PS_MODE_ACTIVE)
78  smart_ps = 0;
79  if ((pwrpriv->pwr_mode != ps_mode) || (pwrpriv->smart_ps != smart_ps)) {
80  if (pwrpriv->pwr_mode == PS_MODE_ACTIVE)
81  pwrpriv->bSleep = true;
82  else
83  pwrpriv->bSleep = false;
84  pwrpriv->pwr_mode = ps_mode;
85  pwrpriv->smart_ps = smart_ps;
86  _set_workitem(&(pwrpriv->SetPSModeWorkItem));
87  }
88 }
89 
90 /*
91  * Caller:ISR handler...
92  *
93  * This will be called when CPWM interrupt is up.
94  *
95  * using to update cpwn of drv; and drv will make a decision to up or
96  * down pwr level
97  */
98 void r8712_cpwm_int_hdl(struct _adapter *padapter,
99  struct reportpwrstate_parm *preportpwrstate)
100 {
101  struct pwrctrl_priv *pwrpriv = &(padapter->pwrctrlpriv);
102  struct cmd_priv *pcmdpriv = &(padapter->cmdpriv);
103 
104  if (pwrpriv->cpwm_tog == ((preportpwrstate->state) & 0x80))
105  return;
106  _cancel_timer_ex(&padapter->pwrctrlpriv. rpwm_check_timer);
107  _enter_pwrlock(&pwrpriv->lock);
108  pwrpriv->cpwm = (preportpwrstate->state) & 0xf;
109  if (pwrpriv->cpwm >= PS_STATE_S2) {
110  if (pwrpriv->alives & CMD_ALIVE)
111  up(&(pcmdpriv->cmd_queue_sema));
112  }
113  pwrpriv->cpwm_tog = (preportpwrstate->state) & 0x80;
114  up(&pwrpriv->lock);
115 }
116 
117 static inline void register_task_alive(struct pwrctrl_priv *pwrctrl, uint tag)
118 {
119  pwrctrl->alives |= tag;
120 }
121 
122 static inline void unregister_task_alive(struct pwrctrl_priv *pwrctrl, uint tag)
123 {
124  if (pwrctrl->alives & tag)
125  pwrctrl->alives ^= tag;
126 }
127 
128 static void _rpwm_check_handler (struct _adapter *padapter)
129 {
130  struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
131 
132  if (padapter->bDriverStopped == true ||
133  padapter->bSurpriseRemoved == true)
134  return;
135  if (pwrpriv->cpwm != pwrpriv->rpwm)
136  _set_workitem(&(pwrpriv->rpwm_workitem));
137 }
138 
139 static void SetPSModeWorkItemCallback(struct work_struct *work)
140 {
141  struct pwrctrl_priv *pwrpriv = container_of(work,
143  struct _adapter *padapter = container_of(pwrpriv,
144  struct _adapter, pwrctrlpriv);
145  if (!pwrpriv->bSleep) {
146  _enter_pwrlock(&pwrpriv->lock);
147  if (pwrpriv->pwr_mode == PS_MODE_ACTIVE)
148  r8712_set_rpwm(padapter, PS_STATE_S4);
149  up(&pwrpriv->lock);
150  }
151 }
152 
153 static void rpwm_workitem_callback(struct work_struct *work)
154 {
155  struct pwrctrl_priv *pwrpriv = container_of(work,
156  struct pwrctrl_priv, rpwm_workitem);
157  struct _adapter *padapter = container_of(pwrpriv,
158  struct _adapter, pwrctrlpriv);
159  u8 cpwm = pwrpriv->cpwm;
160  if (pwrpriv->cpwm != pwrpriv->rpwm) {
161  _enter_pwrlock(&pwrpriv->lock);
162  cpwm = r8712_read8(padapter, SDIO_HCPWM);
163  pwrpriv->rpwm_retry = 1;
164  r8712_set_rpwm(padapter, pwrpriv->rpwm);
165  up(&pwrpriv->lock);
166  }
167 }
168 
169 static void rpwm_check_handler (void *FunctionContext)
170 {
171  struct _adapter *adapter = (struct _adapter *)FunctionContext;
172  _rpwm_check_handler(adapter);
173 }
174 
175 void r8712_init_pwrctrl_priv(struct _adapter *padapter)
176 {
177  struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv;
178 
179  memset((unsigned char *)pwrctrlpriv, 0, sizeof(struct pwrctrl_priv));
180  sema_init(&pwrctrlpriv->lock, 1);
181  pwrctrlpriv->cpwm = PS_STATE_S4;
182  pwrctrlpriv->pwr_mode = PS_MODE_ACTIVE;
183  pwrctrlpriv->smart_ps = 0;
184  pwrctrlpriv->tog = 0x80;
185 /* clear RPWM to ensure driver and fw back to initial state. */
186  r8712_write8(padapter, 0x1025FE58, 0);
187  _init_workitem(&(pwrctrlpriv->SetPSModeWorkItem),
188  SetPSModeWorkItemCallback, padapter);
189  _init_workitem(&(pwrctrlpriv->rpwm_workitem),
190  rpwm_workitem_callback, padapter);
191  _init_timer(&(pwrctrlpriv->rpwm_check_timer),
192  padapter->pnetdev, rpwm_check_handler, (u8 *)padapter);
193 }
194 
195 /*
196 Caller: r8712_cmd_thread
197 
198 Check if the fw_pwrstate is okay for issuing cmd.
199 If not (cpwm should be is less than P2 state), then the sub-routine
200 will raise the cpwm to be greater than or equal to P2.
201 
202 Calling Context: Passive
203 
204 Return Value:
205 
206 _SUCCESS: r8712_cmd_thread can issue cmds to firmware afterwards.
207 _FAIL: r8712_cmd_thread can not do anything.
208 */
210 {
211  uint res = _SUCCESS;
212  struct pwrctrl_priv *pwrctrl = &padapter->pwrctrlpriv;
213 
214  _enter_pwrlock(&pwrctrl->lock);
215  register_task_alive(pwrctrl, CMD_ALIVE);
216  if (pwrctrl->cpwm < PS_STATE_S2) {
217  r8712_set_rpwm(padapter, PS_STATE_S3);
218  res = _FAIL;
219  }
220  up(&pwrctrl->lock);
221  return res;
222 }
223 
224 /*
225 Caller: ISR
226 
227 If ISR's txdone,
228 No more pkts for TX,
229 Then driver shall call this fun. to power down firmware again.
230 */
231 
232 void r8712_unregister_cmd_alive(struct _adapter *padapter)
233 {
234  struct pwrctrl_priv *pwrctrl = &padapter->pwrctrlpriv;
235 
236  _enter_pwrlock(&pwrctrl->lock);
237  unregister_task_alive(pwrctrl, CMD_ALIVE);
238  if ((pwrctrl->cpwm > PS_STATE_S2) &&
239  (pwrctrl->pwr_mode > PS_MODE_ACTIVE)) {
240  if ((pwrctrl->alives == 0) &&
241  (check_fwstate(&padapter->mlmepriv,
242  _FW_UNDER_LINKING) != true)) {
243  r8712_set_rpwm(padapter, PS_STATE_S0);
244  }
245  }
246  up(&pwrctrl->lock);
247 }