These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / staging / rtl8712 / rtl8712_efuse.c
1 /*
2  * rtl8712_efuse.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 <wlanfae@realtek.com>.
25  * Larry Finger <Larry.Finger@lwfinger.net>
26  *
27  ******************************************************************************/
28
29 #define _RTL8712_EFUSE_C_
30
31 #include "osdep_service.h"
32 #include "drv_types.h"
33 #include "rtl8712_efuse.h"
34
35 /* reserve 3 bytes for HW stop read */
36 static int efuse_available_max_size = EFUSE_MAX_SIZE - 3 /*0x1FD*/;
37
38 static void efuse_reg_ctrl(struct _adapter *padapter, u8 bPowerOn)
39 {
40         u8 tmpu8 = 0;
41
42         if (bPowerOn) {
43                 /* -----------------e-fuse pwr & clk reg ctrl ---------------
44                  * Enable LDOE25 Macro Block
45                  */
46                 tmpu8 = r8712_read8(padapter, EFUSE_TEST + 3);
47                 tmpu8 |= 0x80;
48                 r8712_write8(padapter, EFUSE_TEST + 3, tmpu8);
49                 msleep(20); /* for some platform , need some delay time */
50                 /* Change Efuse Clock for write action to 40MHZ */
51                 r8712_write8(padapter, EFUSE_CLK_CTRL, 0x03);
52                 msleep(20); /* for some platform , need some delay time */
53         } else {
54                 /* -----------------e-fuse pwr & clk reg ctrl -----------------
55                  * Disable LDOE25 Macro Block
56                  */
57                 tmpu8 = r8712_read8(padapter, EFUSE_TEST + 3);
58                 tmpu8 &= 0x7F;
59                 r8712_write8(padapter, EFUSE_TEST + 3, tmpu8);
60                 /* Change Efuse Clock for write action to 500K */
61                 r8712_write8(padapter, EFUSE_CLK_CTRL, 0x02);
62         }
63 }
64
65 /*
66  * Before write E-Fuse, this function must be called.
67  */
68 u8 r8712_efuse_reg_init(struct _adapter *padapter)
69 {
70         return true;
71 }
72
73 void r8712_efuse_reg_uninit(struct _adapter *padapter)
74 {
75         efuse_reg_ctrl(padapter, false);
76 }
77
78 static u8 efuse_one_byte_read(struct _adapter *padapter, u16 addr, u8 *data)
79 {
80         u8 tmpidx = 0, bResult;
81
82         /* -----------------e-fuse reg ctrl --------------------------------- */
83         r8712_write8(padapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
84         r8712_write8(padapter, EFUSE_CTRL + 2, ((u8)((addr >> 8) & 0x03)) |
85                (r8712_read8(padapter, EFUSE_CTRL + 2) & 0xFC));
86         r8712_write8(padapter, EFUSE_CTRL + 3, 0x72); /* read cmd */
87         /* wait for complete */
88         while (!(0x80 & r8712_read8(padapter, EFUSE_CTRL + 3)) &&
89                (tmpidx < 100))
90                 tmpidx++;
91         if (tmpidx < 100) {
92                 *data = r8712_read8(padapter, EFUSE_CTRL);
93                 bResult = true;
94         } else {
95                 *data = 0xff;
96                 bResult = false;
97         }
98         return bResult;
99 }
100
101 static u8 efuse_one_byte_write(struct _adapter *padapter, u16 addr, u8 data)
102 {
103         u8 tmpidx = 0, bResult;
104
105         /* -----------------e-fuse reg ctrl -------------------------------- */
106         r8712_write8(padapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
107         r8712_write8(padapter, EFUSE_CTRL + 2, ((u8)((addr >> 8) & 0x03)) |
108                (r8712_read8(padapter, EFUSE_CTRL + 2) & 0xFC));
109         r8712_write8(padapter, EFUSE_CTRL, data); /* data */
110         r8712_write8(padapter, EFUSE_CTRL + 3, 0xF2); /* write cmd */
111         /* wait for complete */
112         while ((0x80 &  r8712_read8(padapter, EFUSE_CTRL + 3)) &&
113                (tmpidx < 100))
114                 tmpidx++;
115         if (tmpidx < 100)
116                 bResult = true;
117         else
118                 bResult = false;
119         return bResult;
120 }
121
122 static u8 efuse_one_byte_rw(struct _adapter *padapter, u8 bRead, u16 addr,
123                             u8 *data)
124 {
125         u8 tmpidx = 0, tmpv8 = 0, bResult;
126
127         /* -----------------e-fuse reg ctrl --------------------------------- */
128         r8712_write8(padapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
129         tmpv8 = ((u8)((addr >> 8) & 0x03)) |
130                  (r8712_read8(padapter, EFUSE_CTRL + 2) & 0xFC);
131         r8712_write8(padapter, EFUSE_CTRL + 2, tmpv8);
132         if (bRead) {
133                 r8712_write8(padapter, EFUSE_CTRL + 3,  0x72); /* read cmd */
134                 while (!(0x80 & r8712_read8(padapter, EFUSE_CTRL + 3)) &&
135                        (tmpidx < 100))
136                         tmpidx++;
137                 if (tmpidx < 100) {
138                         *data = r8712_read8(padapter, EFUSE_CTRL);
139                         bResult = true;
140                 } else {
141                         *data = 0;
142                         bResult = false;
143                 }
144         } else {
145                 r8712_write8(padapter, EFUSE_CTRL, *data); /* data */
146                 r8712_write8(padapter, EFUSE_CTRL + 3, 0xF2); /* write cmd */
147                 while ((0x80 & r8712_read8(padapter, EFUSE_CTRL + 3)) &&
148                        (tmpidx < 100))
149                         tmpidx++;
150                 if (tmpidx < 100)
151                         bResult = true;
152                 else
153                         bResult = false;
154         }
155         return bResult;
156 }
157
158 static u8 efuse_is_empty(struct _adapter *padapter, u8 *empty)
159 {
160         u8 value, ret = true;
161
162         /* read one byte to check if E-Fuse is empty */
163         if (efuse_one_byte_rw(padapter, true, 0, &value)) {
164                 if (0xFF == value)
165                         *empty = true;
166                 else
167                         *empty = false;
168         } else {
169                 ret = false;
170         }
171         return ret;
172 }
173
174 void r8712_efuse_change_max_size(struct _adapter *padapter)
175 {
176         u16 pre_pg_data_saddr = 0x1FB;
177         u16 i;
178         u16 pre_pg_data_size = 5;
179         u8 pre_pg_data[5];
180
181         for (i = 0; i < pre_pg_data_size; i++)
182                 efuse_one_byte_read(padapter, pre_pg_data_saddr + i,
183                                     &pre_pg_data[i]);
184         if ((pre_pg_data[0] == 0x03) && (pre_pg_data[1] == 0x00) &&
185             (pre_pg_data[2] == 0x00) && (pre_pg_data[3] == 0x00) &&
186             (pre_pg_data[4] == 0x0C))
187                 efuse_available_max_size -= pre_pg_data_size;
188 }
189
190 int r8712_efuse_get_max_size(struct _adapter *padapter)
191 {
192         return  efuse_available_max_size;
193 }
194
195 static u8 calculate_word_cnts(const u8 word_en)
196 {
197         u8 word_cnts = 0;
198         u8 word_idx;
199
200         for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++)
201                 if (!(word_en & BIT(word_idx)))
202                         word_cnts++; /* 0 : write enable */
203         return word_cnts;
204 }
205
206 static void pgpacket_copy_data(const u8 word_en, const u8 *sourdata,
207                                u8 *targetdata)
208 {
209         u8 tmpindex = 0;
210         u8 word_idx, byte_idx;
211
212         for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++) {
213                 if (!(word_en & BIT(word_idx))) {
214                         byte_idx = word_idx * 2;
215                         targetdata[byte_idx] = sourdata[tmpindex++];
216                         targetdata[byte_idx + 1] = sourdata[tmpindex++];
217                 }
218         }
219 }
220
221 u16 r8712_efuse_get_current_size(struct _adapter *padapter)
222 {
223         int bContinual = true;
224         u16 efuse_addr = 0;
225         u8 hworden = 0;
226         u8 efuse_data, word_cnts = 0;
227
228         while (bContinual && efuse_one_byte_read(padapter, efuse_addr,
229                &efuse_data) && (efuse_addr < efuse_available_max_size)) {
230                 if (efuse_data != 0xFF) {
231                         hworden =  efuse_data & 0x0F;
232                         word_cnts = calculate_word_cnts(hworden);
233                         /* read next header */
234                         efuse_addr = efuse_addr + (word_cnts * 2) + 1;
235                 } else {
236                         bContinual = false;
237                 }
238         }
239         return efuse_addr;
240 }
241
242 u8 r8712_efuse_pg_packet_read(struct _adapter *padapter, u8 offset, u8 *data)
243 {
244         u8 hoffset = 0, hworden = 0, word_cnts = 0;
245         u16 efuse_addr = 0;
246         u8 efuse_data;
247         u8 tmpidx = 0;
248         u8 tmpdata[PGPKT_DATA_SIZE];
249         u8 ret = true;
250
251         if (data == NULL)
252                 return false;
253         if (offset > 0x0f)
254                 return false;
255         memset(data, 0xFF, sizeof(u8) * PGPKT_DATA_SIZE);
256         while (efuse_addr < efuse_available_max_size) {
257                 if (efuse_one_byte_read(padapter, efuse_addr, &efuse_data)) {
258                         if (efuse_data == 0xFF)
259                                 break;
260                         hoffset = (efuse_data >> 4) & 0x0F;
261                         hworden =  efuse_data & 0x0F;
262                         word_cnts = calculate_word_cnts(hworden);
263                         if (hoffset == offset) {
264                                 memset(tmpdata, 0xFF, PGPKT_DATA_SIZE);
265                                 for (tmpidx = 0; tmpidx < word_cnts * 2;
266                                      tmpidx++) {
267                                         if (efuse_one_byte_read(padapter,
268                                             efuse_addr + 1 + tmpidx,
269                                             &efuse_data)) {
270                                                 tmpdata[tmpidx] = efuse_data;
271                                         } else {
272                                                 ret = false;
273                                         }
274                                 }
275                                 pgpacket_copy_data(hworden, tmpdata, data);
276                         }
277                         efuse_addr += 1 + (word_cnts * 2);
278                 } else {
279                         ret = false;
280                         break;
281                 }
282         }
283         return ret;
284 }
285
286 static u8 fix_header(struct _adapter *padapter, u8 header, u16 header_addr)
287 {
288         struct PGPKT_STRUCT pkt;
289         u8 offset, word_en, value;
290         u16 addr;
291         int i;
292         u8 ret = true;
293
294         pkt.offset = GET_EFUSE_OFFSET(header);
295         pkt.word_en = GET_EFUSE_WORD_EN(header);
296         addr = header_addr + 1 + calculate_word_cnts(pkt.word_en) * 2;
297         if (addr > efuse_available_max_size)
298                 return false;
299         /* retrieve original data */
300         addr = 0;
301         while (addr < header_addr) {
302                 if (!efuse_one_byte_read(padapter, addr++, &value)) {
303                         ret = false;
304                         break;
305                 }
306                 offset = GET_EFUSE_OFFSET(value);
307                 word_en = GET_EFUSE_WORD_EN(value);
308                 if (pkt.offset != offset) {
309                         addr += calculate_word_cnts(word_en) * 2;
310                         continue;
311                 }
312                 for (i = 0; i < PGPKG_MAX_WORDS; i++) {
313                         if (BIT(i) & word_en) {
314                                 if (BIT(i) & pkt.word_en) {
315                                         if (efuse_one_byte_read(
316                                                         padapter, addr,
317                                                         &value))
318                                                 pkt.data[i * 2] = value;
319                                         else
320                                                 return false;
321                                         if (efuse_one_byte_read(
322                                                         padapter,
323                                                         addr + 1,
324                                                         &value))
325                                                 pkt.data[i * 2 + 1] =
326                                                         value;
327                                         else
328                                                 return false;
329                                 }
330                                 addr += 2;
331                         }
332                 }
333         }
334         if (addr != header_addr)
335                 return false;
336         addr++;
337         /* fill original data */
338         for (i = 0; i < PGPKG_MAX_WORDS; i++) {
339                 if (BIT(i) & pkt.word_en) {
340                         efuse_one_byte_write(padapter, addr, pkt.data[i * 2]);
341                         efuse_one_byte_write(padapter, addr + 1,
342                                              pkt.data[i * 2 + 1]);
343                         /* additional check */
344                         if (!efuse_one_byte_read(padapter, addr, &value)) {
345                                 ret = false;
346                         } else if (pkt.data[i * 2] != value) {
347                                 ret = false;
348                                 if (0xFF == value) /* write again */
349                                         efuse_one_byte_write(padapter, addr,
350                                                         pkt.data[i * 2]);
351                         }
352                         if (!efuse_one_byte_read(padapter, addr + 1, &value)) {
353                                 ret = false;
354                         } else if (pkt.data[i * 2 + 1] != value) {
355                                 ret = false;
356                                 if (0xFF == value) /* write again */
357                                         efuse_one_byte_write(padapter, addr + 1,
358                                                              pkt.data[i * 2 +
359                                                                       1]);
360                         }
361                 }
362                 addr += 2;
363         }
364         return ret;
365 }
366
367 u8 r8712_efuse_pg_packet_write(struct _adapter *padapter, const u8 offset,
368                          const u8 word_en, const u8 *data)
369 {
370         u8 pg_header = 0;
371         u16 efuse_addr = 0, curr_size = 0;
372         u8 efuse_data, target_word_cnts = 0;
373         static int repeat_times;
374         int sub_repeat;
375         u8 bResult = true;
376
377         /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
378         efuse_data = r8712_read8(padapter, EFUSE_CLK_CTRL);
379         if (efuse_data != 0x03)
380                 return false;
381         pg_header = MAKE_EFUSE_HEADER(offset, word_en);
382         target_word_cnts = calculate_word_cnts(word_en);
383         repeat_times = 0;
384         efuse_addr = 0;
385         while (efuse_addr < efuse_available_max_size) {
386                 curr_size = r8712_efuse_get_current_size(padapter);
387                 if ((curr_size + 1 + target_word_cnts * 2) >
388                      efuse_available_max_size)
389                         return false; /*target_word_cnts + pg header(1 byte)*/
390                 efuse_addr = curr_size; /* current size is also the last addr*/
391                 efuse_one_byte_write(padapter, efuse_addr, pg_header); /*hdr*/
392                 sub_repeat = 0;
393                 /* check if what we read is what we write */
394                 while (!efuse_one_byte_read(padapter, efuse_addr,
395                                             &efuse_data)) {
396                         if (++sub_repeat > _REPEAT_THRESHOLD_) {
397                                 bResult = false; /* continue to blind write */
398                                 break; /* continue to blind write */
399                         }
400                 }
401                 if ((sub_repeat > _REPEAT_THRESHOLD_) ||
402                     (pg_header == efuse_data)) {
403                         /* write header ok OR can't check header(creep) */
404                         u8 i;
405
406                         /* go to next address */
407                         efuse_addr++;
408                         for (i = 0; i < target_word_cnts * 2; i++) {
409                                 efuse_one_byte_write(padapter,
410                                                      efuse_addr + i,
411                                                      *(data + i));
412                                 if (!efuse_one_byte_read(padapter,
413                                                          efuse_addr + i,
414                                                          &efuse_data))
415                                         bResult = false;
416                                 else if (*(data + i) != efuse_data) /* fail */
417                                         bResult = false;
418                         }
419                         break;
420                 }
421                 /* write header fail */
422                 bResult = false;
423                 if (0xFF == efuse_data)
424                         return bResult; /* nothing damaged. */
425                 /* call rescue procedure */
426                 if (!fix_header(padapter, efuse_data, efuse_addr))
427                         return false; /* rescue fail */
428
429                 if (++repeat_times > _REPEAT_THRESHOLD_) /* fail */
430                         break;
431                 /* otherwise, take another risk... */
432         }
433         return bResult;
434 }
435
436 u8 r8712_efuse_access(struct _adapter *padapter, u8 bRead, u16 start_addr,
437                       u16 cnts, u8 *data)
438 {
439         int i;
440         u8 res = true;
441
442         if (start_addr > EFUSE_MAX_SIZE)
443                 return false;
444         if (!bRead && ((start_addr + cnts) >
445            efuse_available_max_size))
446                 return false;
447         if (!bRead && !r8712_efuse_reg_init(padapter))
448                 return false;
449         /* -----------------e-fuse one byte read / write ---------------------*/
450         for (i = 0; i < cnts; i++) {
451                 if ((start_addr + i) > EFUSE_MAX_SIZE) {
452                         res = false;
453                         break;
454                 }
455                 res = efuse_one_byte_rw(padapter, bRead, start_addr + i,
456                       data + i);
457                 if (!bRead && !res)
458                         break;
459         }
460         if (!bRead)
461                 r8712_efuse_reg_uninit(padapter);
462         return res;
463 }
464
465 u8 r8712_efuse_map_read(struct _adapter *padapter, u16 addr, u16 cnts, u8 *data)
466 {
467         u8 offset, ret = true;
468         u8 pktdata[PGPKT_DATA_SIZE];
469         int i, idx;
470
471         if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
472                 return false;
473         if (efuse_is_empty(padapter, &offset) && offset) {
474                 for (i = 0; i < cnts; i++)
475                         data[i] = 0xFF;
476                 return ret;
477         }
478         offset = (addr >> 3) & 0xF;
479         ret = r8712_efuse_pg_packet_read(padapter, offset, pktdata);
480         i = addr & 0x7; /* pktdata index */
481         idx = 0;        /* data index */
482
483         do {
484                 for (; i < PGPKT_DATA_SIZE; i++) {
485                         data[idx++] = pktdata[i];
486                         if (idx == cnts)
487                                 return ret;
488                 }
489                 offset++;
490                 if (!r8712_efuse_pg_packet_read(padapter, offset, pktdata))
491                         ret = false;
492                 i = 0;
493         } while (1);
494         return ret;
495 }
496
497 u8 r8712_efuse_map_write(struct _adapter *padapter, u16 addr, u16 cnts,
498                          u8 *data)
499 {
500         u8 offset, word_en, empty;
501         u8 pktdata[PGPKT_DATA_SIZE], newdata[PGPKT_DATA_SIZE];
502         int i, j, idx;
503
504         if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
505                 return false;
506         /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
507         empty = r8712_read8(padapter, EFUSE_CLK_CTRL);
508         if (empty != 0x03)
509                 return false;
510         if (efuse_is_empty(padapter, &empty)) {
511                 if (empty)
512                         memset(pktdata, 0xFF, PGPKT_DATA_SIZE);
513         } else {
514                 return false;
515         }
516         offset = (addr >> 3) & 0xF;
517         if (!empty)
518                 if (!r8712_efuse_pg_packet_read(padapter, offset, pktdata))
519                         return false;
520         word_en = 0xF;
521         memset(newdata, 0xFF, PGPKT_DATA_SIZE);
522         i = addr & 0x7; /* pktdata index */
523         j = 0;          /* newdata index */
524         idx = 0;        /* data index */
525
526         if (i & 0x1) {
527                 /*  odd start */
528                 if (data[idx] != pktdata[i]) {
529                         word_en &= ~BIT(i >> 1);
530                         newdata[j++] = pktdata[i - 1];
531                         newdata[j++] = data[idx];
532                 }
533                 i++;
534                 idx++;
535         }
536         do {
537                 for (; i < PGPKT_DATA_SIZE; i += 2) {
538                         if ((cnts - idx) == 1) {
539                                 if (data[idx] != pktdata[i]) {
540                                         word_en &= ~BIT(i >> 1);
541                                         newdata[j++] = data[idx];
542                                         newdata[j++] = pktdata[1 + 1];
543                                 }
544                                 idx++;
545                                 break;
546                         }
547
548                         if ((data[idx] != pktdata[i]) || (data[idx + 1] !=
549                              pktdata[i + 1])) {
550                                 word_en &= ~BIT(i >> 1);
551                                 newdata[j++] = data[idx];
552                                 newdata[j++] = data[idx + 1];
553                         }
554                         idx += 2;
555
556                         if (idx == cnts)
557                                 break;
558                 }
559
560                 if (word_en != 0xF)
561                         if (!r8712_efuse_pg_packet_write(padapter, offset,
562                                                          word_en, newdata))
563                                 return false;
564                 if (idx == cnts)
565                         break;
566                 offset++;
567                 if (!empty)
568                         if (!r8712_efuse_pg_packet_read(padapter, offset,
569                             pktdata))
570                                 return false;
571                 i = 0;
572                 j = 0;
573                 word_en = 0xF;
574                 memset(newdata, 0xFF, PGPKT_DATA_SIZE);
575         } while (1);
576
577         return true;
578 }