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 */ 24static int efuse_available_max_size = EFUSE_MAX_SIZE - 3 /*0x1FD*/; 25 26static 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 */ 56u8 r8712_efuse_reg_init(struct _adapter *adapter) 57{ 58 return true; 59} 60 61void r8712_efuse_reg_uninit(struct _adapter *adapter) 62{ 63 efuse_reg_ctrl(adapter, false); 64} 65 66static 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 89static 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 110static 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 146static 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 162void 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 178int r8712_efuse_get_max_size(struct _adapter *adapter) 179{ 180 return efuse_available_max_size; 181} 182 183static 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 194static 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 209u16 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, &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; 223 } else { 224 bContinual = false; 225 } 226 } 227 return efuse_addr; 228} 229 230u8 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, efuse_addr + 1 + tmpidx, 256 &efuse_data)) { 257 tmpdata[tmpidx] = efuse_data; 258 } else { 259 ret = false; 260 } 261 } 262 pgpacket_copy_data(hworden, tmpdata, data); 263 } 264 efuse_addr += 1 + (word_cnts * 2); 265 } else { 266 ret = false; 267 break; 268 } 269 } 270 return ret; 271} 272 273static u8 fix_header(struct _adapter *adapter, u8 header, u16 header_addr) 274{ 275 struct PGPKT_STRUCT pkt; 276 u8 offset, word_en, value; 277 u16 addr; 278 int i; 279 u8 ret = true; 280 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) 285 return false; 286 /* retrieve original data */ 287 addr = 0; 288 while (addr < header_addr) { 289 if (!efuse_one_byte_read(adapter, addr++, &value)) { 290 ret = false; 291 break; 292 } 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; 297 continue; 298 } 299 for (i = 0; i < PGPKG_MAX_WORDS; i++) { 300 if (!(BIT(i) & word_en)) 301 continue; 302 if (BIT(i) & pkt.word_en) { 303 if (efuse_one_byte_read(adapter, 304 addr, 305 &value)) 306 pkt.data[i * 2] = value; 307 else 308 return false; 309 if (efuse_one_byte_read(adapter, 310 addr + 1, 311 &value)) 312 pkt.data[i * 2 + 1] = value; 313 else 314 return false; 315 } 316 addr += 2; 317 } 318 } 319 if (addr != header_addr) 320 return false; 321 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)) { 330 ret = false; 331 } else if (pkt.data[i * 2] != value) { 332 ret = false; 333 if (value == 0xFF) /* write again */ 334 efuse_one_byte_write(adapter, addr, 335 pkt.data[i * 2]); 336 } 337 if (!efuse_one_byte_read(adapter, addr + 1, &value)) { 338 ret = false; 339 } else if (pkt.data[i * 2 + 1] != value) { 340 ret = false; 341 if (value == 0xFF) /* write again */ 342 efuse_one_byte_write(adapter, addr + 1, 343 pkt.data[i * 2 + 344 1]); 345 } 346 } 347 addr += 2; 348 } 349 return ret; 350} 351 352u8 r8712_efuse_pg_packet_write(struct _adapter *adapter, const u8 offset, 353 const u8 word_en, const u8 *data) 354{ 355 u8 pg_header = 0; 356 u16 efuse_addr = 0, curr_size = 0; 357 u8 efuse_data, target_word_cnts = 0; 358 int repeat_times; 359 int sub_repeat; 360 u8 bResult = true; 361 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) 365 return false; 366 pg_header = MAKE_EFUSE_HEADER(offset, word_en); 367 target_word_cnts = calculate_word_cnts(word_en); 368 repeat_times = 0; 369 efuse_addr = 0; 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*/ 377 sub_repeat = 0; 378 /* check if what we read is what we write */ 379 while (!efuse_one_byte_read(adapter, efuse_addr, 380 &efuse_data)) { 381 if (++sub_repeat > _REPEAT_THRESHOLD_) { 382 bResult = false; /* continue to blind write */ 383 break; /* continue to blind write */ 384 } 385 } 386 if ((sub_repeat > _REPEAT_THRESHOLD_) || 387 (pg_header == efuse_data)) { 388 /* write header ok OR can't check header(creep) */ 389 u8 i; 390 391 /* go to next address */ 392 efuse_addr++; 393 for (i = 0; i < target_word_cnts * 2; i++) { 394 efuse_one_byte_write(adapter, 395 efuse_addr + i, 396 *(data + i)); 397 if (!efuse_one_byte_read(adapter, 398 efuse_addr + i, 399 &efuse_data)) 400 bResult = false; 401 else if (*(data + i) != efuse_data) /* fail */ 402 bResult = false; 403 } 404 break; 405 } 406 /* write header fail */ 407 bResult = false; 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 */ 413 414 if (++repeat_times > _REPEAT_THRESHOLD_) /* fail */ 415 break; 416 /* otherwise, take another risk... */ 417 } 418 return bResult; 419} 420 421u8 r8712_efuse_access(struct _adapter *adapter, u8 bRead, u16 start_addr, 422 u16 cnts, u8 *data) 423{ 424 int i; 425 u8 res = true; 426 427 if (start_addr > EFUSE_MAX_SIZE) 428 return false; 429 if (!bRead && ((start_addr + cnts) > 430 efuse_available_max_size)) 431 return false; 432 if (!bRead && !r8712_efuse_reg_init(adapter)) 433 return false; 434 /* -----------------e-fuse one byte read / write ---------------------*/ 435 for (i = 0; i < cnts; i++) { 436 if ((start_addr + i) > EFUSE_MAX_SIZE) { 437 res = false; 438 break; 439 } 440 res = efuse_one_byte_rw(adapter, bRead, start_addr + i, 441 data + i); 442 if (!bRead && !res) 443 break; 444 } 445 if (!bRead) 446 r8712_efuse_reg_uninit(adapter); 447 return res; 448} 449 450u8 r8712_efuse_map_read(struct _adapter *adapter, u16 addr, u16 cnts, u8 *data) 451{ 452 u8 offset, ret = true; 453 u8 pktdata[PGPKT_DATA_SIZE]; 454 int i, idx; 455 456 if ((addr + cnts) > EFUSE_MAP_MAX_SIZE) 457 return false; 458 if (efuse_is_empty(adapter, &offset) && offset) { 459 for (i = 0; i < cnts; i++) 460 data[i] = 0xFF; 461 return ret; 462 } 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 */ 467 468 do { 469 for (; i < PGPKT_DATA_SIZE; i++) { 470 data[idx++] = pktdata[i]; 471 if (idx == cnts) 472 return ret; 473 } 474 offset++; 475 if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata)) 476 ret = false; 477 i = 0; 478 } while (1); 479 return ret; 480} 481 482u8 r8712_efuse_map_write(struct _adapter *adapter, u16 addr, u16 cnts, 483 u8 *data) 484{ 485 u8 offset, word_en, empty; 486 u8 pktdata[PGPKT_DATA_SIZE], newdata[PGPKT_DATA_SIZE]; 487 int i, j, idx; 488 489 if ((addr + cnts) > EFUSE_MAP_MAX_SIZE) 490 return false; 491 /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */ 492 empty = r8712_read8(adapter, EFUSE_CLK_CTRL); 493 if (empty != 0x03) 494 return false; 495 if (efuse_is_empty(adapter, &empty)) { 496 if (empty) 497 memset(pktdata, 0xFF, PGPKT_DATA_SIZE); 498 } else { 499 return false; 500 } 501 offset = (addr >> 3) & 0xF; 502 if (!empty) 503 if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata)) 504 return false; 505 word_en = 0xF; 506 memset(newdata, 0xFF, PGPKT_DATA_SIZE); 507 i = addr & 0x7; /* pktdata index */ 508 j = 0; /* newdata index */ 509 idx = 0; /* data index */ 510 511 if (i & 0x1) { 512 /* odd start */ 513 if (data[idx] != pktdata[i]) { 514 word_en &= ~BIT(i >> 1); 515 newdata[j++] = pktdata[i - 1]; 516 newdata[j++] = data[idx]; 517 } 518 i++; 519 idx++; 520 } 521 do { 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]; 528 } 529 idx++; 530 break; 531 } 532 533 if ((data[idx] != pktdata[i]) || (data[idx + 1] != 534 pktdata[i + 1])) { 535 word_en &= ~BIT(i >> 1); 536 newdata[j++] = data[idx]; 537 newdata[j++] = data[idx + 1]; 538 } 539 idx += 2; 540 541 if (idx == cnts) 542 break; 543 } 544 545 if (word_en != 0xF) 546 if (!r8712_efuse_pg_packet_write(adapter, offset, 547 word_en, newdata)) 548 return false; 549 if (idx == cnts) 550 break; 551 offset++; 552 if (!empty) 553 if (!r8712_efuse_pg_packet_read(adapter, offset, 554 pktdata)) 555 return false; 556 i = 0; 557 j = 0; 558 word_en = 0xF; 559 memset(newdata, 0xFF, PGPKT_DATA_SIZE); 560 } while (1); 561 562 return true; 563} 564