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