Merge tag 'i2c-for-5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa...
[linux-2.6-microblaze.git] / drivers / staging / r8188eu / core / rtw_led.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright(c) 2007 - 2012 Realtek Corporation. */
3
4 #include "../include/drv_types.h"
5 #include "../include/rtw_led.h"
6 #include "../include/rtl8188e_spec.h"
7
8 #define LED_BLINK_NO_LINK_INTVL                 msecs_to_jiffies(1000)
9 #define LED_BLINK_LINK_INTVL                    msecs_to_jiffies(500)
10 #define LED_BLINK_SCAN_INTVL                    msecs_to_jiffies(180)
11 #define LED_BLINK_FASTER_INTVL                  msecs_to_jiffies(50)
12 #define LED_BLINK_WPS_SUCESS_INTVL              msecs_to_jiffies(5000)
13
14 #define IS_LED_WPS_BLINKING(l) \
15         ((l)->CurrLedState == LED_BLINK_WPS || \
16         (l)->CurrLedState == LED_BLINK_WPS_STOP || \
17         (l)->bLedWPSBlinkInProgress)
18
19 static void ResetLedStatus(struct LED_871x *pLed)
20 {
21         pLed->CurrLedState = RTW_LED_OFF; /*  Current LED state. */
22         pLed->bLedOn = false; /*  true if LED is ON, false if LED is OFF. */
23
24         pLed->bLedBlinkInProgress = false; /*  true if it is blinking, false o.w.. */
25         pLed->bLedWPSBlinkInProgress = false;
26
27         pLed->BlinkTimes = 0; /*  Number of times to toggle led state for blinking. */
28         pLed->BlinkingLedState = LED_UNKNOWN; /*  Next state for blinking, either RTW_LED_ON or RTW_LED_OFF are. */
29
30         pLed->bLedNoLinkBlinkInProgress = false;
31         pLed->bLedLinkBlinkInProgress = false;
32         pLed->bLedScanBlinkInProgress = false;
33 }
34
35 static void SwLedOn(struct adapter *padapter, struct LED_871x *pLed)
36 {
37         u8      LedCfg;
38
39         if (padapter->bSurpriseRemoved || padapter->bDriverStopped)
40                 return;
41
42         LedCfg = rtw_read8(padapter, REG_LEDCFG2);
43         rtw_write8(padapter, REG_LEDCFG2, (LedCfg & 0xf0) | BIT(5) | BIT(6)); /*  SW control led0 on. */
44         pLed->bLedOn = true;
45 }
46
47 static void SwLedOff(struct adapter *padapter, struct LED_871x *pLed)
48 {
49         u8      LedCfg;
50
51         if (padapter->bSurpriseRemoved || padapter->bDriverStopped)
52                 goto exit;
53
54         LedCfg = rtw_read8(padapter, REG_LEDCFG2);/* 0x4E */
55
56         LedCfg &= 0x90; /*  Set to software control. */
57         rtw_write8(padapter, REG_LEDCFG2, (LedCfg | BIT(3)));
58         LedCfg = rtw_read8(padapter, REG_MAC_PINMUX_CFG);
59         LedCfg &= 0xFE;
60         rtw_write8(padapter, REG_MAC_PINMUX_CFG, LedCfg);
61 exit:
62         pLed->bLedOn = false;
63 }
64
65 static void blink_work(struct work_struct *work)
66 {
67         struct delayed_work *dwork = to_delayed_work(work);
68         struct LED_871x *pLed = container_of(dwork, struct LED_871x, blink_work);
69         struct adapter *padapter = pLed->padapter;
70         struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
71
72         if ((padapter->bSurpriseRemoved) || (padapter->bDriverStopped))
73                 return;
74
75         if (padapter->pwrctrlpriv.rf_pwrstate != rf_on) {
76                 SwLedOff(padapter, pLed);
77                 ResetLedStatus(pLed);
78                 return;
79         }
80
81         /*  Change LED according to BlinkingLedState specified. */
82         if (pLed->BlinkingLedState == RTW_LED_ON)
83                 SwLedOn(padapter, pLed);
84         else
85                 SwLedOff(padapter, pLed);
86
87         switch (pLed->CurrLedState) {
88         case LED_BLINK_SLOWLY:
89                 if (pLed->bLedOn)
90                         pLed->BlinkingLedState = RTW_LED_OFF;
91                 else
92                         pLed->BlinkingLedState = RTW_LED_ON;
93                 schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
94                 break;
95         case LED_BLINK_NORMAL:
96                 if (pLed->bLedOn)
97                         pLed->BlinkingLedState = RTW_LED_OFF;
98                 else
99                         pLed->BlinkingLedState = RTW_LED_ON;
100                 schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
101                 break;
102         case LED_BLINK_SCAN:
103                 if (pLed->bLedOn)
104                         pLed->BlinkingLedState = RTW_LED_OFF;
105                 else
106                         pLed->BlinkingLedState = RTW_LED_ON;
107                 pLed->BlinkTimes--;
108                 if (pLed->BlinkTimes == 0) {
109                         if (check_fwstate(pmlmepriv, _FW_LINKED)) {
110                                 pLed->bLedLinkBlinkInProgress = true;
111                                 pLed->CurrLedState = LED_BLINK_NORMAL;
112                                 schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
113                         } else {
114                                 pLed->bLedNoLinkBlinkInProgress = true;
115                                 pLed->CurrLedState = LED_BLINK_SLOWLY;
116                                 schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
117                         }
118                         pLed->bLedScanBlinkInProgress = false;
119                 } else {
120                         schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
121                 }
122                 break;
123         case LED_BLINK_TXRX:
124                 if (pLed->bLedOn)
125                         pLed->BlinkingLedState = RTW_LED_OFF;
126                 else
127                         pLed->BlinkingLedState = RTW_LED_ON;
128                 pLed->BlinkTimes--;
129                 if (pLed->BlinkTimes == 0) {
130                         if (check_fwstate(pmlmepriv, _FW_LINKED)) {
131                                 pLed->bLedLinkBlinkInProgress = true;
132                                 pLed->CurrLedState = LED_BLINK_NORMAL;
133                                 schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
134                         } else {
135                                 pLed->bLedNoLinkBlinkInProgress = true;
136                                 pLed->CurrLedState = LED_BLINK_SLOWLY;
137                                 schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
138                         }
139                         pLed->bLedBlinkInProgress = false;
140                 } else {
141                         schedule_delayed_work(&pLed->blink_work, LED_BLINK_FASTER_INTVL);
142                 }
143                 break;
144         case LED_BLINK_WPS:
145                 if (pLed->bLedOn)
146                         pLed->BlinkingLedState = RTW_LED_OFF;
147                 else
148                         pLed->BlinkingLedState = RTW_LED_ON;
149                 schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
150                 break;
151         case LED_BLINK_WPS_STOP:        /* WPS success */
152                 if (pLed->BlinkingLedState != RTW_LED_ON) {
153                         pLed->bLedLinkBlinkInProgress = true;
154                         pLed->CurrLedState = LED_BLINK_NORMAL;
155                         if (pLed->bLedOn)
156                                 pLed->BlinkingLedState = RTW_LED_OFF;
157                         else
158                                 pLed->BlinkingLedState = RTW_LED_ON;
159                         schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
160
161                         pLed->bLedWPSBlinkInProgress = false;
162                 } else {
163                         pLed->BlinkingLedState = RTW_LED_OFF;
164                         schedule_delayed_work(&pLed->blink_work, LED_BLINK_WPS_SUCESS_INTVL);
165                 }
166                 break;
167         default:
168                 break;
169         }
170 }
171
172 void rtl8188eu_InitSwLeds(struct adapter *padapter)
173 {
174         struct led_priv *pledpriv = &padapter->ledpriv;
175         struct LED_871x *pLed = &pledpriv->SwLed0;
176
177         pLed->padapter = padapter;
178         ResetLedStatus(pLed);
179         INIT_DELAYED_WORK(&pLed->blink_work, blink_work);
180 }
181
182 void rtl8188eu_DeInitSwLeds(struct adapter *padapter)
183 {
184         struct led_priv *ledpriv = &padapter->ledpriv;
185         struct LED_871x *pLed = &ledpriv->SwLed0;
186
187         cancel_delayed_work_sync(&pLed->blink_work);
188         ResetLedStatus(pLed);
189         SwLedOff(padapter, pLed);
190 }
191
192 void rtw_led_control(struct adapter *padapter, enum LED_CTL_MODE LedAction)
193 {
194         struct led_priv *ledpriv = &padapter->ledpriv;
195         struct registry_priv *registry_par;
196         struct LED_871x *pLed = &ledpriv->SwLed0;
197         struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
198
199         if ((padapter->bSurpriseRemoved) || (padapter->bDriverStopped) ||
200             (!padapter->hw_init_completed))
201                 return;
202
203         if (!ledpriv->bRegUseLed)
204                 return;
205
206         registry_par = &padapter->registrypriv;
207         if (!registry_par->led_enable)
208                 return;
209
210         switch (LedAction) {
211         case LED_CTL_START_TO_LINK:
212         case LED_CTL_NO_LINK:
213                 if (!pLed->bLedNoLinkBlinkInProgress) {
214                         if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
215                                 return;
216                         if (pLed->bLedLinkBlinkInProgress) {
217                                 cancel_delayed_work(&pLed->blink_work);
218                                 pLed->bLedLinkBlinkInProgress = false;
219                         }
220                         if (pLed->bLedBlinkInProgress) {
221                                 cancel_delayed_work(&pLed->blink_work);
222                                 pLed->bLedBlinkInProgress = false;
223                         }
224
225                         pLed->bLedNoLinkBlinkInProgress = true;
226                         pLed->CurrLedState = LED_BLINK_SLOWLY;
227                         if (pLed->bLedOn)
228                                 pLed->BlinkingLedState = RTW_LED_OFF;
229                         else
230                                 pLed->BlinkingLedState = RTW_LED_ON;
231                         schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
232                 }
233                 break;
234         case LED_CTL_LINK:
235                 if (!pLed->bLedLinkBlinkInProgress) {
236                         if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
237                                 return;
238                         if (pLed->bLedNoLinkBlinkInProgress) {
239                                 cancel_delayed_work(&pLed->blink_work);
240                                 pLed->bLedNoLinkBlinkInProgress = false;
241                         }
242                         if (pLed->bLedBlinkInProgress) {
243                                 cancel_delayed_work(&pLed->blink_work);
244                                 pLed->bLedBlinkInProgress = false;
245                         }
246                         pLed->bLedLinkBlinkInProgress = true;
247                         pLed->CurrLedState = LED_BLINK_NORMAL;
248                         if (pLed->bLedOn)
249                                 pLed->BlinkingLedState = RTW_LED_OFF;
250                         else
251                                 pLed->BlinkingLedState = RTW_LED_ON;
252                         schedule_delayed_work(&pLed->blink_work, LED_BLINK_LINK_INTVL);
253                 }
254                 break;
255         case LED_CTL_SITE_SURVEY:
256                 if ((pmlmepriv->LinkDetectInfo.bBusyTraffic) && (check_fwstate(pmlmepriv, _FW_LINKED))) {
257                         ;
258                 } else if (!pLed->bLedScanBlinkInProgress) {
259                         if (IS_LED_WPS_BLINKING(pLed))
260                                 return;
261                         if (pLed->bLedNoLinkBlinkInProgress) {
262                                 cancel_delayed_work(&pLed->blink_work);
263                                 pLed->bLedNoLinkBlinkInProgress = false;
264                         }
265                         if (pLed->bLedLinkBlinkInProgress) {
266                                 cancel_delayed_work(&pLed->blink_work);
267                                 pLed->bLedLinkBlinkInProgress = false;
268                         }
269                         if (pLed->bLedBlinkInProgress) {
270                                 cancel_delayed_work(&pLed->blink_work);
271                                 pLed->bLedBlinkInProgress = false;
272                         }
273                         pLed->bLedScanBlinkInProgress = true;
274                         pLed->CurrLedState = LED_BLINK_SCAN;
275                         pLed->BlinkTimes = 24;
276                         if (pLed->bLedOn)
277                                 pLed->BlinkingLedState = RTW_LED_OFF;
278                         else
279                                 pLed->BlinkingLedState = RTW_LED_ON;
280                         schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
281                 }
282                 break;
283         case LED_CTL_TX:
284         case LED_CTL_RX:
285                 if (!pLed->bLedBlinkInProgress) {
286                         if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
287                                 return;
288                         if (pLed->bLedNoLinkBlinkInProgress) {
289                                 cancel_delayed_work(&pLed->blink_work);
290                                 pLed->bLedNoLinkBlinkInProgress = false;
291                         }
292                         if (pLed->bLedLinkBlinkInProgress) {
293                                 cancel_delayed_work(&pLed->blink_work);
294                                 pLed->bLedLinkBlinkInProgress = false;
295                         }
296                         pLed->bLedBlinkInProgress = true;
297                         pLed->CurrLedState = LED_BLINK_TXRX;
298                         pLed->BlinkTimes = 2;
299                         if (pLed->bLedOn)
300                                 pLed->BlinkingLedState = RTW_LED_OFF;
301                         else
302                                 pLed->BlinkingLedState = RTW_LED_ON;
303                         schedule_delayed_work(&pLed->blink_work, LED_BLINK_FASTER_INTVL);
304                 }
305                 break;
306         case LED_CTL_START_WPS: /* wait until xinpin finish */
307                 if (!pLed->bLedWPSBlinkInProgress) {
308                         if (pLed->bLedNoLinkBlinkInProgress) {
309                                 cancel_delayed_work(&pLed->blink_work);
310                                 pLed->bLedNoLinkBlinkInProgress = false;
311                         }
312                         if (pLed->bLedLinkBlinkInProgress) {
313                                 cancel_delayed_work(&pLed->blink_work);
314                                 pLed->bLedLinkBlinkInProgress = false;
315                         }
316                         if (pLed->bLedBlinkInProgress) {
317                                 cancel_delayed_work(&pLed->blink_work);
318                                 pLed->bLedBlinkInProgress = false;
319                         }
320                         if (pLed->bLedScanBlinkInProgress) {
321                                 cancel_delayed_work(&pLed->blink_work);
322                                 pLed->bLedScanBlinkInProgress = false;
323                         }
324                         pLed->bLedWPSBlinkInProgress = true;
325                         pLed->CurrLedState = LED_BLINK_WPS;
326                         if (pLed->bLedOn)
327                                 pLed->BlinkingLedState = RTW_LED_OFF;
328                         else
329                                 pLed->BlinkingLedState = RTW_LED_ON;
330                         schedule_delayed_work(&pLed->blink_work, LED_BLINK_SCAN_INTVL);
331                 }
332                 break;
333         case LED_CTL_STOP_WPS:
334                 if (pLed->bLedNoLinkBlinkInProgress) {
335                         cancel_delayed_work(&pLed->blink_work);
336                         pLed->bLedNoLinkBlinkInProgress = false;
337                 }
338                 if (pLed->bLedLinkBlinkInProgress) {
339                         cancel_delayed_work(&pLed->blink_work);
340                         pLed->bLedLinkBlinkInProgress = false;
341                 }
342                 if (pLed->bLedBlinkInProgress) {
343                         cancel_delayed_work(&pLed->blink_work);
344                         pLed->bLedBlinkInProgress = false;
345                 }
346                 if (pLed->bLedScanBlinkInProgress) {
347                         cancel_delayed_work(&pLed->blink_work);
348                         pLed->bLedScanBlinkInProgress = false;
349                 }
350                 if (pLed->bLedWPSBlinkInProgress)
351                         cancel_delayed_work(&pLed->blink_work);
352                 else
353                         pLed->bLedWPSBlinkInProgress = true;
354                 pLed->CurrLedState = LED_BLINK_WPS_STOP;
355                 if (pLed->bLedOn) {
356                         pLed->BlinkingLedState = RTW_LED_OFF;
357                         schedule_delayed_work(&pLed->blink_work, LED_BLINK_WPS_SUCESS_INTVL);
358                 } else {
359                         pLed->BlinkingLedState = RTW_LED_ON;
360                         schedule_delayed_work(&pLed->blink_work, 0);
361                 }
362                 break;
363         case LED_CTL_STOP_WPS_FAIL:
364                 if (pLed->bLedWPSBlinkInProgress) {
365                         cancel_delayed_work(&pLed->blink_work);
366                         pLed->bLedWPSBlinkInProgress = false;
367                 }
368                 pLed->bLedNoLinkBlinkInProgress = true;
369                 pLed->CurrLedState = LED_BLINK_SLOWLY;
370                 if (pLed->bLedOn)
371                         pLed->BlinkingLedState = RTW_LED_OFF;
372                 else
373                         pLed->BlinkingLedState = RTW_LED_ON;
374                 schedule_delayed_work(&pLed->blink_work, LED_BLINK_NO_LINK_INTVL);
375                 break;
376         case LED_CTL_POWER_OFF:
377                 pLed->CurrLedState = RTW_LED_OFF;
378                 pLed->BlinkingLedState = RTW_LED_OFF;
379                 if (pLed->bLedNoLinkBlinkInProgress) {
380                         cancel_delayed_work(&pLed->blink_work);
381                         pLed->bLedNoLinkBlinkInProgress = false;
382                 }
383                 if (pLed->bLedLinkBlinkInProgress) {
384                         cancel_delayed_work(&pLed->blink_work);
385                         pLed->bLedLinkBlinkInProgress = false;
386                 }
387                 if (pLed->bLedBlinkInProgress) {
388                         cancel_delayed_work(&pLed->blink_work);
389                         pLed->bLedBlinkInProgress = false;
390                 }
391                 if (pLed->bLedWPSBlinkInProgress) {
392                         cancel_delayed_work(&pLed->blink_work);
393                         pLed->bLedWPSBlinkInProgress = false;
394                 }
395                 if (pLed->bLedScanBlinkInProgress) {
396                         cancel_delayed_work(&pLed->blink_work);
397                         pLed->bLedScanBlinkInProgress = false;
398                 }
399                 SwLedOff(padapter, pLed);
400                 break;
401         default:
402                 break;
403         }
404 }