rtl8712_efuse.c (14543B)
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, 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 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, 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 274static 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 continue; 303 if (BIT(i) & pkt.word_en) { 304 if (efuse_one_byte_read(adapter, 305 addr, 306 &value)) 307 pkt.data[i * 2] = value; 308 else 309 return false; 310 if (efuse_one_byte_read(adapter, 311 addr + 1, 312 &value)) 313 pkt.data[i * 2 + 1] = value; 314 else 315 return false; 316 } 317 addr += 2; 318 } 319 } 320 if (addr != header_addr) 321 return false; 322 addr++; 323 /* fill original data */ 324 for (i = 0; i < PGPKG_MAX_WORDS; i++) { 325 if (BIT(i) & pkt.word_en) { 326 efuse_one_byte_write(adapter, addr, pkt.data[i * 2]); 327 efuse_one_byte_write(adapter, addr + 1, 328 pkt.data[i * 2 + 1]); 329 /* additional check */ 330 if (!efuse_one_byte_read(adapter, addr, &value)) { 331 ret = false; 332 } else if (pkt.data[i * 2] != value) { 333 ret = false; 334 if (value == 0xFF) /* write again */ 335 efuse_one_byte_write(adapter, addr, 336 pkt.data[i * 2]); 337 } 338 if (!efuse_one_byte_read(adapter, addr + 1, &value)) { 339 ret = false; 340 } else if (pkt.data[i * 2 + 1] != value) { 341 ret = false; 342 if (value == 0xFF) /* write again */ 343 efuse_one_byte_write(adapter, addr + 1, 344 pkt.data[i * 2 + 345 1]); 346 } 347 } 348 addr += 2; 349 } 350 return ret; 351} 352 353u8 r8712_efuse_pg_packet_write(struct _adapter *adapter, const u8 offset, 354 const u8 word_en, const u8 *data) 355{ 356 u8 pg_header = 0; 357 u16 efuse_addr = 0, curr_size = 0; 358 u8 efuse_data, target_word_cnts = 0; 359 int repeat_times; 360 int sub_repeat; 361 u8 bResult = true; 362 363 /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */ 364 efuse_data = r8712_read8(adapter, EFUSE_CLK_CTRL); 365 if (efuse_data != 0x03) 366 return false; 367 pg_header = MAKE_EFUSE_HEADER(offset, word_en); 368 target_word_cnts = calculate_word_cnts(word_en); 369 repeat_times = 0; 370 efuse_addr = 0; 371 while (efuse_addr < efuse_available_max_size) { 372 curr_size = r8712_efuse_get_current_size(adapter); 373 if ((curr_size + 1 + target_word_cnts * 2) > 374 efuse_available_max_size) 375 return false; /*target_word_cnts + pg header(1 byte)*/ 376 efuse_addr = curr_size; /* current size is also the last addr*/ 377 efuse_one_byte_write(adapter, efuse_addr, pg_header); /*hdr*/ 378 sub_repeat = 0; 379 /* check if what we read is what we write */ 380 while (!efuse_one_byte_read(adapter, efuse_addr, 381 &efuse_data)) { 382 if (++sub_repeat > _REPEAT_THRESHOLD_) { 383 bResult = false; /* continue to blind write */ 384 break; /* continue to blind write */ 385 } 386 } 387 if ((sub_repeat > _REPEAT_THRESHOLD_) || 388 (pg_header == efuse_data)) { 389 /* write header ok OR can't check header(creep) */ 390 u8 i; 391 392 /* go to next address */ 393 efuse_addr++; 394 for (i = 0; i < target_word_cnts * 2; i++) { 395 efuse_one_byte_write(adapter, 396 efuse_addr + i, 397 *(data + i)); 398 if (!efuse_one_byte_read(adapter, 399 efuse_addr + i, 400 &efuse_data)) 401 bResult = false; 402 else if (*(data + i) != efuse_data) /* fail */ 403 bResult = false; 404 } 405 break; 406 } 407 /* write header fail */ 408 bResult = false; 409 if (efuse_data == 0xFF) 410 return bResult; /* nothing damaged. */ 411 /* call rescue procedure */ 412 if (!fix_header(adapter, efuse_data, efuse_addr)) 413 return false; /* rescue fail */ 414 415 if (++repeat_times > _REPEAT_THRESHOLD_) /* fail */ 416 break; 417 /* otherwise, take another risk... */ 418 } 419 return bResult; 420} 421 422u8 r8712_efuse_access(struct _adapter *adapter, u8 bRead, u16 start_addr, 423 u16 cnts, u8 *data) 424{ 425 int i; 426 u8 res = true; 427 428 if (start_addr > EFUSE_MAX_SIZE) 429 return false; 430 if (!bRead && ((start_addr + cnts) > 431 efuse_available_max_size)) 432 return false; 433 if (!bRead && !r8712_efuse_reg_init(adapter)) 434 return false; 435 /* -----------------e-fuse one byte read / write ---------------------*/ 436 for (i = 0; i < cnts; i++) { 437 if ((start_addr + i) > EFUSE_MAX_SIZE) { 438 res = false; 439 break; 440 } 441 res = efuse_one_byte_rw(adapter, bRead, start_addr + i, 442 data + i); 443 if (!bRead && !res) 444 break; 445 } 446 if (!bRead) 447 r8712_efuse_reg_uninit(adapter); 448 return res; 449} 450 451u8 r8712_efuse_map_read(struct _adapter *adapter, u16 addr, u16 cnts, u8 *data) 452{ 453 u8 offset, ret = true; 454 u8 pktdata[PGPKT_DATA_SIZE]; 455 int i, idx; 456 457 if ((addr + cnts) > EFUSE_MAP_MAX_SIZE) 458 return false; 459 if (efuse_is_empty(adapter, &offset) && offset) { 460 for (i = 0; i < cnts; i++) 461 data[i] = 0xFF; 462 return ret; 463 } 464 offset = (addr >> 3) & 0xF; 465 ret = r8712_efuse_pg_packet_read(adapter, offset, pktdata); 466 i = addr & 0x7; /* pktdata index */ 467 idx = 0; /* data index */ 468 469 do { 470 for (; i < PGPKT_DATA_SIZE; i++) { 471 data[idx++] = pktdata[i]; 472 if (idx == cnts) 473 return ret; 474 } 475 offset++; 476 if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata)) 477 ret = false; 478 i = 0; 479 } while (1); 480 return ret; 481} 482 483u8 r8712_efuse_map_write(struct _adapter *adapter, u16 addr, u16 cnts, 484 u8 *data) 485{ 486 u8 offset, word_en, empty; 487 u8 pktdata[PGPKT_DATA_SIZE], newdata[PGPKT_DATA_SIZE]; 488 int i, j, idx; 489 490 if ((addr + cnts) > EFUSE_MAP_MAX_SIZE) 491 return false; 492 /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */ 493 empty = r8712_read8(adapter, EFUSE_CLK_CTRL); 494 if (empty != 0x03) 495 return false; 496 if (efuse_is_empty(adapter, &empty)) { 497 if (empty) 498 memset(pktdata, 0xFF, PGPKT_DATA_SIZE); 499 } else { 500 return false; 501 } 502 offset = (addr >> 3) & 0xF; 503 if (!empty) 504 if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata)) 505 return false; 506 word_en = 0xF; 507 memset(newdata, 0xFF, PGPKT_DATA_SIZE); 508 i = addr & 0x7; /* pktdata index */ 509 j = 0; /* newdata index */ 510 idx = 0; /* data index */ 511 512 if (i & 0x1) { 513 /* odd start */ 514 if (data[idx] != pktdata[i]) { 515 word_en &= ~BIT(i >> 1); 516 newdata[j++] = pktdata[i - 1]; 517 newdata[j++] = data[idx]; 518 } 519 i++; 520 idx++; 521 } 522 do { 523 for (; i < PGPKT_DATA_SIZE; i += 2) { 524 if ((cnts - idx) == 1) { 525 if (data[idx] != pktdata[i]) { 526 word_en &= ~BIT(i >> 1); 527 newdata[j++] = data[idx]; 528 newdata[j++] = pktdata[1 + 1]; 529 } 530 idx++; 531 break; 532 } 533 534 if ((data[idx] != pktdata[i]) || (data[idx + 1] != 535 pktdata[i + 1])) { 536 word_en &= ~BIT(i >> 1); 537 newdata[j++] = data[idx]; 538 newdata[j++] = data[idx + 1]; 539 } 540 idx += 2; 541 542 if (idx == cnts) 543 break; 544 } 545 546 if (word_en != 0xF) 547 if (!r8712_efuse_pg_packet_write(adapter, offset, 548 word_en, newdata)) 549 return false; 550 if (idx == cnts) 551 break; 552 offset++; 553 if (!empty) 554 if (!r8712_efuse_pg_packet_read(adapter, offset, 555 pktdata)) 556 return false; 557 i = 0; 558 j = 0; 559 word_en = 0xF; 560 memset(newdata, 0xFF, PGPKT_DATA_SIZE); 561 } while (1); 562 563 return true; 564}