1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
6 * Linux device driver for RTL8192SU
8 * Modifications for inclusion into the Linux staging tree are
9 * Copyright(c) 2010 Larry Finger. All rights reserved.
11 * Contact information:
12 * WLAN FAE <wlanfae@realtek.com>.
13 * Larry Finger <Larry.Finger@lwfinger.net>
15 ******************************************************************************/
17 #define _RTL8712_EFUSE_C_
19 #include "osdep_service.h"
20 #include "drv_types.h"
21 #include "rtl8712_efuse.h"
23 /* reserve 3 bytes for HW stop read */
24 static int efuse_available_max_size = EFUSE_MAX_SIZE - 3 /*0x1FD*/;
26 static void efuse_reg_ctrl(struct _adapter *adapter, u8 bPowerOn)
31 /* -----------------e-fuse pwr & clk reg ctrl ---------------
32 * Enable LDOE25 Macro Block
34 tmpu8 = r8712_read8(adapter, EFUSE_TEST + 3);
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 */
42 /* -----------------e-fuse pwr & clk reg ctrl -----------------
43 * Disable LDOE25 Macro Block
45 tmpu8 = r8712_read8(adapter, EFUSE_TEST + 3);
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);
54 * Before write E-Fuse, this function must be called.
56 u8 r8712_efuse_reg_init(struct _adapter *adapter)
61 void r8712_efuse_reg_uninit(struct _adapter *adapter)
63 efuse_reg_ctrl(adapter, false);
66 static u8 efuse_one_byte_read(struct _adapter *adapter, u16 addr, u8 *data)
68 u8 tmpidx = 0, bResult;
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)) &&
80 *data = r8712_read8(adapter, EFUSE_CTRL);
89 static u8 efuse_one_byte_write(struct _adapter *adapter, u16 addr, u8 data)
91 u8 tmpidx = 0, bResult;
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)) &&
110 static u8 efuse_one_byte_rw(struct _adapter *adapter, u8 bRead, u16 addr,
113 u8 tmpidx = 0, tmpv8 = 0, bResult;
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);
121 r8712_write8(adapter, EFUSE_CTRL + 3, 0x72); /* read cmd */
122 while (!(0x80 & r8712_read8(adapter, EFUSE_CTRL + 3)) &&
126 *data = r8712_read8(adapter, EFUSE_CTRL);
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)) &&
146 static u8 efuse_is_empty(struct _adapter *adapter, u8 *empty)
148 u8 value, ret = true;
150 /* read one byte to check if E-Fuse is empty */
151 if (efuse_one_byte_rw(adapter, true, 0, &value)) {
162 void r8712_efuse_change_max_size(struct _adapter *adapter)
164 u16 pre_pg_data_saddr = 0x1FB;
166 u16 pre_pg_data_size = 5;
169 for (i = 0; i < pre_pg_data_size; i++)
170 efuse_one_byte_read(adapter, pre_pg_data_saddr + 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;
178 int r8712_efuse_get_max_size(struct _adapter *adapter)
180 return efuse_available_max_size;
183 static u8 calculate_word_cnts(const u8 word_en)
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 */
194 static void pgpacket_copy_data(const u8 word_en, const u8 *sourdata,
198 u8 word_idx, byte_idx;
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++];
209 u16 r8712_efuse_get_current_size(struct _adapter *adapter)
211 int bContinual = true;
214 u8 efuse_data, word_cnts = 0;
216 while (bContinual && efuse_one_byte_read(adapter, efuse_addr, &efuse_data) &&
217 (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;
230 u8 r8712_efuse_pg_packet_read(struct _adapter *adapter, u8 offset, u8 *data)
232 u8 hoffset = 0, hworden = 0, word_cnts = 0;
236 u8 tmpdata[PGPKT_DATA_SIZE];
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)
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;
255 if (efuse_one_byte_read(adapter, efuse_addr + 1 + tmpidx,
257 tmpdata[tmpidx] = efuse_data;
262 pgpacket_copy_data(hworden, tmpdata, data);
264 efuse_addr += 1 + (word_cnts * 2);
273 static u8 fix_header(struct _adapter *adapter, u8 header, u16 header_addr)
275 struct PGPKT_STRUCT pkt;
276 u8 offset, word_en, value;
281 pkt.offset = GET_EFUSE_OFFSET(header);
282 pkt.word_en = GET_EFUSE_WORD_EN(header);
283 addr = header_addr + 1 + calculate_word_cnts(pkt.word_en) * 2;
284 if (addr > efuse_available_max_size)
286 /* retrieve original data */
288 while (addr < header_addr) {
289 if (!efuse_one_byte_read(adapter, addr++, &value)) {
293 offset = GET_EFUSE_OFFSET(value);
294 word_en = GET_EFUSE_WORD_EN(value);
295 if (pkt.offset != offset) {
296 addr += calculate_word_cnts(word_en) * 2;
299 for (i = 0; i < PGPKG_MAX_WORDS; i++) {
300 if (!(BIT(i) & word_en))
302 if (BIT(i) & pkt.word_en) {
303 if (efuse_one_byte_read(adapter,
306 pkt.data[i * 2] = value;
309 if (efuse_one_byte_read(adapter,
312 pkt.data[i * 2 + 1] = value;
319 if (addr != header_addr)
322 /* fill original data */
323 for (i = 0; i < PGPKG_MAX_WORDS; i++) {
324 if (BIT(i) & pkt.word_en) {
325 efuse_one_byte_write(adapter, addr, pkt.data[i * 2]);
326 efuse_one_byte_write(adapter, addr + 1,
327 pkt.data[i * 2 + 1]);
328 /* additional check */
329 if (!efuse_one_byte_read(adapter, addr, &value)) {
331 } else if (pkt.data[i * 2] != value) {
333 if (value == 0xFF) /* write again */
334 efuse_one_byte_write(adapter, addr,
337 if (!efuse_one_byte_read(adapter, addr + 1, &value)) {
339 } else if (pkt.data[i * 2 + 1] != value) {
341 if (value == 0xFF) /* write again */
342 efuse_one_byte_write(adapter, addr + 1,
352 u8 r8712_efuse_pg_packet_write(struct _adapter *adapter, const u8 offset,
353 const u8 word_en, const u8 *data)
356 u16 efuse_addr = 0, curr_size = 0;
357 u8 efuse_data, target_word_cnts = 0;
362 /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
363 efuse_data = r8712_read8(adapter, EFUSE_CLK_CTRL);
364 if (efuse_data != 0x03)
366 pg_header = MAKE_EFUSE_HEADER(offset, word_en);
367 target_word_cnts = calculate_word_cnts(word_en);
370 while (efuse_addr < efuse_available_max_size) {
371 curr_size = r8712_efuse_get_current_size(adapter);
372 if ((curr_size + 1 + target_word_cnts * 2) >
373 efuse_available_max_size)
374 return false; /*target_word_cnts + pg header(1 byte)*/
375 efuse_addr = curr_size; /* current size is also the last addr*/
376 efuse_one_byte_write(adapter, efuse_addr, pg_header); /*hdr*/
378 /* check if what we read is what we write */
379 while (!efuse_one_byte_read(adapter, efuse_addr,
381 if (++sub_repeat > _REPEAT_THRESHOLD_) {
382 bResult = false; /* continue to blind write */
383 break; /* continue to blind write */
386 if ((sub_repeat > _REPEAT_THRESHOLD_) ||
387 (pg_header == efuse_data)) {
388 /* write header ok OR can't check header(creep) */
391 /* go to next address */
393 for (i = 0; i < target_word_cnts * 2; i++) {
394 efuse_one_byte_write(adapter,
397 if (!efuse_one_byte_read(adapter,
401 else if (*(data + i) != efuse_data) /* fail */
406 /* write header fail */
408 if (efuse_data == 0xFF)
409 return bResult; /* nothing damaged. */
410 /* call rescue procedure */
411 if (!fix_header(adapter, efuse_data, efuse_addr))
412 return false; /* rescue fail */
414 if (++repeat_times > _REPEAT_THRESHOLD_) /* fail */
416 /* otherwise, take another risk... */
421 u8 r8712_efuse_access(struct _adapter *adapter, u8 bRead, u16 start_addr,
427 if (start_addr > EFUSE_MAX_SIZE)
429 if (!bRead && ((start_addr + cnts) >
430 efuse_available_max_size))
432 if (!bRead && !r8712_efuse_reg_init(adapter))
434 /* -----------------e-fuse one byte read / write ---------------------*/
435 for (i = 0; i < cnts; i++) {
436 if ((start_addr + i) > EFUSE_MAX_SIZE) {
440 res = efuse_one_byte_rw(adapter, bRead, start_addr + i,
446 r8712_efuse_reg_uninit(adapter);
450 u8 r8712_efuse_map_read(struct _adapter *adapter, u16 addr, u16 cnts, u8 *data)
452 u8 offset, ret = true;
453 u8 pktdata[PGPKT_DATA_SIZE];
456 if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
458 if (efuse_is_empty(adapter, &offset) && offset) {
459 for (i = 0; i < cnts; i++)
463 offset = (addr >> 3) & 0xF;
464 ret = r8712_efuse_pg_packet_read(adapter, offset, pktdata);
465 i = addr & 0x7; /* pktdata index */
466 idx = 0; /* data index */
469 for (; i < PGPKT_DATA_SIZE; i++) {
470 data[idx++] = pktdata[i];
475 if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata))
482 u8 r8712_efuse_map_write(struct _adapter *adapter, u16 addr, u16 cnts,
485 u8 offset, word_en, empty;
486 u8 pktdata[PGPKT_DATA_SIZE], newdata[PGPKT_DATA_SIZE];
489 if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
491 /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
492 empty = r8712_read8(adapter, EFUSE_CLK_CTRL);
495 if (efuse_is_empty(adapter, &empty)) {
497 memset(pktdata, 0xFF, PGPKT_DATA_SIZE);
501 offset = (addr >> 3) & 0xF;
503 if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata))
506 memset(newdata, 0xFF, PGPKT_DATA_SIZE);
507 i = addr & 0x7; /* pktdata index */
508 j = 0; /* newdata index */
509 idx = 0; /* data index */
513 if (data[idx] != pktdata[i]) {
514 word_en &= ~BIT(i >> 1);
515 newdata[j++] = pktdata[i - 1];
516 newdata[j++] = data[idx];
522 for (; i < PGPKT_DATA_SIZE; i += 2) {
523 if ((cnts - idx) == 1) {
524 if (data[idx] != pktdata[i]) {
525 word_en &= ~BIT(i >> 1);
526 newdata[j++] = data[idx];
527 newdata[j++] = pktdata[1 + 1];
533 if ((data[idx] != pktdata[i]) || (data[idx + 1] !=
535 word_en &= ~BIT(i >> 1);
536 newdata[j++] = data[idx];
537 newdata[j++] = data[idx + 1];
546 if (!r8712_efuse_pg_packet_write(adapter, offset,
553 if (!r8712_efuse_pg_packet_read(adapter, offset,
559 memset(newdata, 0xFF, PGPKT_DATA_SIZE);