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