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