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 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 */ 56 u8 r8712_efuse_reg_init(struct _adapter *adapter) 57 { 58 return true; 59 } 60 61 void r8712_efuse_reg_uninit(struct _adapter *adapter) 62 { 63 efuse_reg_ctrl(adapter, false); 64 } 65 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 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 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 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 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 178 int r8712_efuse_get_max_size(struct _adapter *adapter) 179 { 180 return efuse_available_max_size; 181 } 182 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 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 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 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 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 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 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 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 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