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
efuse_reg_ctrl(struct _adapter * padapter,u8 bPowerOn)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 */
r8712_efuse_reg_init(struct _adapter * padapter)68 u8 r8712_efuse_reg_init(struct _adapter *padapter)
69 {
70 return true;
71 }
72
r8712_efuse_reg_uninit(struct _adapter * padapter)73 void r8712_efuse_reg_uninit(struct _adapter *padapter)
74 {
75 efuse_reg_ctrl(padapter, false);
76 }
77
efuse_one_byte_read(struct _adapter * padapter,u16 addr,u8 * data)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
efuse_one_byte_write(struct _adapter * padapter,u16 addr,u8 data)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
efuse_one_byte_rw(struct _adapter * padapter,u8 bRead,u16 addr,u8 * data)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
efuse_is_empty(struct _adapter * padapter,u8 * empty)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
r8712_efuse_change_max_size(struct _adapter * padapter)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
r8712_efuse_get_max_size(struct _adapter * padapter)187 int r8712_efuse_get_max_size(struct _adapter *padapter)
188 {
189 return efuse_available_max_size;
190 }
191
calculate_word_cnts(const u8 word_en)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
pgpacket_copy_data(const u8 word_en,const u8 * sourdata,u8 * targetdata)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
r8712_efuse_get_current_size(struct _adapter * padapter)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
r8712_efuse_pg_packet_read(struct _adapter * padapter,u8 offset,u8 * data)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
fix_header(struct _adapter * padapter,u8 header,u16 header_addr)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
r8712_efuse_pg_packet_write(struct _adapter * padapter,const u8 offset,const u8 word_en,const u8 * data)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
r8712_efuse_access(struct _adapter * padapter,u8 bRead,u16 start_addr,u16 cnts,u8 * data)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
r8712_efuse_map_read(struct _adapter * padapter,u16 addr,u16 cnts,u8 * data)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
r8712_efuse_map_write(struct _adapter * padapter,u16 addr,u16 cnts,u8 * data)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 }
569