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 (true == 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)) && (tmpidx < 100)) 89 tmpidx++; 90 if (tmpidx < 100) { 91 *data = r8712_read8(padapter, EFUSE_CTRL); 92 bResult = true; 93 } else { 94 *data = 0xff; 95 bResult = false; 96 } 97 return bResult; 98} 99 100static u8 efuse_one_byte_write(struct _adapter *padapter, u16 addr, u8 data) 101{ 102 u8 tmpidx = 0, bResult; 103 104 /* -----------------e-fuse reg ctrl -------------------------------- */ 105 r8712_write8(padapter, EFUSE_CTRL+1, (u8)(addr&0xFF)); /* address */ 106 r8712_write8(padapter, EFUSE_CTRL+2, ((u8)((addr>>8)&0x03)) | 107 (r8712_read8(padapter, EFUSE_CTRL+2)&0xFC)); 108 r8712_write8(padapter, EFUSE_CTRL, data); /* data */ 109 r8712_write8(padapter, EFUSE_CTRL+3, 0xF2); /* write cmd */ 110 /* wait for complete */ 111 while ((0x80 & r8712_read8(padapter, EFUSE_CTRL+3)) && (tmpidx < 100)) 112 tmpidx++; 113 if (tmpidx < 100) 114 bResult = true; 115 else 116 bResult = false; 117 return bResult; 118} 119 120static u8 efuse_one_byte_rw(struct _adapter *padapter, u8 bRead, u16 addr, 121 u8 *data) 122{ 123 u8 tmpidx = 0, tmpv8 = 0, bResult; 124 125 /* -----------------e-fuse reg ctrl --------------------------------- */ 126 r8712_write8(padapter, EFUSE_CTRL+1, (u8)(addr&0xFF)); /* address */ 127 tmpv8 = ((u8)((addr >> 8) & 0x03)) | 128 (r8712_read8(padapter, EFUSE_CTRL + 2) & 0xFC); 129 r8712_write8(padapter, EFUSE_CTRL+2, tmpv8); 130 if (true == bRead) { 131 r8712_write8(padapter, EFUSE_CTRL+3, 0x72); /* read cmd */ 132 while (!(0x80 & r8712_read8(padapter, EFUSE_CTRL+3)) && 133 (tmpidx < 100)) 134 tmpidx++; 135 if (tmpidx < 100) { 136 *data = r8712_read8(padapter, EFUSE_CTRL); 137 bResult = true; 138 } else { 139 *data = 0; 140 bResult = false; 141 } 142 } else { 143 r8712_write8(padapter, EFUSE_CTRL, *data); /* data */ 144 r8712_write8(padapter, EFUSE_CTRL+3, 0xF2); /* write cmd */ 145 while ((0x80 & r8712_read8(padapter, EFUSE_CTRL+3)) && 146 (tmpidx < 100)) 147 tmpidx++; 148 if (tmpidx < 100) 149 bResult = true; 150 else 151 bResult = false; 152 } 153 return bResult; 154} 155 156static u8 efuse_is_empty(struct _adapter *padapter, u8 *empty) 157{ 158 u8 value, ret = true; 159 160 /* read one byte to check if E-Fuse is empty */ 161 if (efuse_one_byte_rw(padapter, true, 0, &value) == true) { 162 if (0xFF == value) 163 *empty = true; 164 else 165 *empty = false; 166 } else 167 ret = false; 168 return ret; 169} 170 171void r8712_efuse_change_max_size(struct _adapter *padapter) 172{ 173 u16 pre_pg_data_saddr = 0x1FB; 174 u16 i; 175 u16 pre_pg_data_size = 5; 176 u8 pre_pg_data[5]; 177 178 for (i = 0; i < pre_pg_data_size; i++) 179 efuse_one_byte_read(padapter, pre_pg_data_saddr + i, 180 &pre_pg_data[i]); 181 if ((pre_pg_data[0] == 0x03) && (pre_pg_data[1] == 0x00) && 182 (pre_pg_data[2] == 0x00) && (pre_pg_data[3] == 0x00) && 183 (pre_pg_data[4] == 0x0C)) 184 efuse_available_max_size -= pre_pg_data_size; 185} 186 187int r8712_efuse_get_max_size(struct _adapter *padapter) 188{ 189 return efuse_available_max_size; 190} 191 192static u8 calculate_word_cnts(const u8 word_en) 193{ 194 u8 word_cnts = 0; 195 u8 word_idx; 196 197 for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++) 198 if (!(word_en & BIT(word_idx))) 199 word_cnts++; /* 0 : write enable */ 200 return word_cnts; 201} 202 203static void pgpacket_copy_data(const u8 word_en, const u8 *sourdata, 204 u8 *targetdata) 205{ 206 u8 tmpindex = 0; 207 u8 word_idx, byte_idx; 208 209 for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++) { 210 if (!(word_en&BIT(word_idx))) { 211 byte_idx = word_idx * 2; 212 targetdata[byte_idx] = sourdata[tmpindex++]; 213 targetdata[byte_idx + 1] = sourdata[tmpindex++]; 214 } 215 } 216} 217 218u16 r8712_efuse_get_current_size(struct _adapter *padapter) 219{ 220 int bContinual = true; 221 u16 efuse_addr = 0; 222 u8 hworden = 0; 223 u8 efuse_data, word_cnts = 0; 224 225 while (bContinual && efuse_one_byte_read(padapter, efuse_addr, 226 &efuse_data) && (efuse_addr < efuse_available_max_size)) { 227 if (efuse_data != 0xFF) { 228 hworden = efuse_data & 0x0F; 229 word_cnts = calculate_word_cnts(hworden); 230 /* read next header */ 231 efuse_addr = efuse_addr + (word_cnts * 2) + 1; 232 } else 233 bContinual = false; 234 } 235 return efuse_addr; 236} 237 238u8 r8712_efuse_pg_packet_read(struct _adapter *padapter, u8 offset, u8 *data) 239{ 240 u8 hoffset = 0, hworden = 0, word_cnts = 0; 241 u16 efuse_addr = 0; 242 u8 efuse_data; 243 u8 tmpidx = 0; 244 u8 tmpdata[PGPKT_DATA_SIZE]; 245 u8 ret = true; 246 247 if (data == NULL) 248 return false; 249 if (offset > 0x0f) 250 return false; 251 memset(data, 0xFF, sizeof(u8)*PGPKT_DATA_SIZE); 252 while (efuse_addr < efuse_available_max_size) { 253 if (efuse_one_byte_read(padapter, efuse_addr, &efuse_data) == 254 true) { 255 if (efuse_data == 0xFF) 256 break; 257 hoffset = (efuse_data >> 4) & 0x0F; 258 hworden = efuse_data & 0x0F; 259 word_cnts = calculate_word_cnts(hworden); 260 if (hoffset == offset) { 261 memset(tmpdata, 0xFF, PGPKT_DATA_SIZE); 262 for (tmpidx = 0; tmpidx < word_cnts * 2; 263 tmpidx++) { 264 if (efuse_one_byte_read(padapter, 265 efuse_addr+1+tmpidx, &efuse_data) == 266 true) { 267 tmpdata[tmpidx] = efuse_data; 268 } else 269 ret = false; 270 } 271 pgpacket_copy_data(hworden, tmpdata, data); 272 } 273 efuse_addr += 1 + (word_cnts*2); 274 } else { 275 ret = false; 276 break; 277 } 278 } 279 return ret; 280} 281 282static u8 fix_header(struct _adapter *padapter, u8 header, u16 header_addr) 283{ 284 struct PGPKT_STRUCT pkt; 285 u8 offset, word_en, value; 286 u16 addr; 287 int i; 288 u8 ret = true; 289 290 pkt.offset = GET_EFUSE_OFFSET(header); 291 pkt.word_en = GET_EFUSE_WORD_EN(header); 292 addr = header_addr + 1 + calculate_word_cnts(pkt.word_en) * 2; 293 if (addr > efuse_available_max_size) 294 return false; 295 /* retrieve original data */ 296 addr = 0; 297 while (addr < header_addr) { 298 if (efuse_one_byte_read(padapter, addr++, &value) == false) { 299 ret = false; 300 break; 301 } 302 offset = GET_EFUSE_OFFSET(value); 303 word_en = GET_EFUSE_WORD_EN(value); 304 if (pkt.offset != offset) { 305 addr += calculate_word_cnts(word_en)*2; 306 continue; 307 } 308 for (i = 0; i < PGPKG_MAX_WORDS; i++) { 309 if (BIT(i) & word_en) { 310 if (BIT(i) & pkt.word_en) { 311 if (efuse_one_byte_read( 312 padapter, addr, 313 &value) == true) 314 pkt.data[i*2] = value; 315 else 316 return false; 317 if (efuse_one_byte_read( 318 padapter, 319 addr + 1, 320 &value) == true) 321 pkt.data[i*2 + 1] = 322 value; 323 else 324 return false; 325 } 326 addr += 2; 327 } 328 } 329 } 330 if (addr != header_addr) 331 return false; 332 addr++; 333 /* fill original data */ 334 for (i = 0; i < PGPKG_MAX_WORDS; i++) { 335 if (BIT(i) & pkt.word_en) { 336 efuse_one_byte_write(padapter, addr, pkt.data[i*2]); 337 efuse_one_byte_write(padapter, addr+1, 338 pkt.data[i*2 + 1]); 339 /* additional check */ 340 if (efuse_one_byte_read(padapter, addr, &value) 341 == false) 342 ret = false; 343 else if (pkt.data[i*2] != value) { 344 ret = false; 345 if (0xFF == value) /* write again */ 346 efuse_one_byte_write(padapter, addr, 347 pkt.data[i * 2]); 348 } 349 if (efuse_one_byte_read(padapter, addr+1, &value) == 350 false) 351 ret = false; 352 else if (pkt.data[i*2 + 1] != value) { 353 ret = false; 354 if (0xFF == value) /* write again */ 355 efuse_one_byte_write(padapter, addr+1, 356 pkt.data[i*2 + 1]); 357 } 358 } 359 addr += 2; 360 } 361 return ret; 362} 363 364u8 r8712_efuse_pg_packet_write(struct _adapter *padapter, const u8 offset, 365 const u8 word_en, const u8 *data) 366{ 367 u8 pg_header = 0; 368 u16 efuse_addr = 0, curr_size = 0; 369 u8 efuse_data, target_word_cnts = 0; 370 static int repeat_times; 371 int sub_repeat; 372 u8 bResult = true; 373 374 /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */ 375 efuse_data = r8712_read8(padapter, EFUSE_CLK_CTRL); 376 if (efuse_data != 0x03) 377 return false; 378 pg_header = MAKE_EFUSE_HEADER(offset, word_en); 379 target_word_cnts = calculate_word_cnts(word_en); 380 repeat_times = 0; 381 efuse_addr = 0; 382 while (efuse_addr < efuse_available_max_size) { 383 curr_size = r8712_efuse_get_current_size(padapter); 384 if ((curr_size + 1 + target_word_cnts * 2) > 385 efuse_available_max_size) 386 return false; /*target_word_cnts + pg header(1 byte)*/ 387 efuse_addr = curr_size; /* current size is also the last addr*/ 388 efuse_one_byte_write(padapter, efuse_addr, pg_header); /*hdr*/ 389 sub_repeat = 0; 390 /* check if what we read is what we write */ 391 while (efuse_one_byte_read(padapter, efuse_addr, 392 &efuse_data) == false) { 393 if (++sub_repeat > _REPEAT_THRESHOLD_) { 394 bResult = false; /* continue to blind write */ 395 break; /* continue to blind write */ 396 } 397 } 398 if ((sub_repeat > _REPEAT_THRESHOLD_) || 399 (pg_header == efuse_data)) { 400 /* write header ok OR can't check header(creep) */ 401 u8 i; 402 403 /* go to next address */ 404 efuse_addr++; 405 for (i = 0; i < target_word_cnts*2; i++) { 406 efuse_one_byte_write(padapter, 407 efuse_addr + i, 408 *(data + i)); 409 if (efuse_one_byte_read(padapter, 410 efuse_addr + i, &efuse_data) == false) 411 bResult = false; 412 else if (*(data+i) != efuse_data) /* fail */ 413 bResult = false; 414 } 415 break; 416 } 417 /* write header fail */ 418 bResult = false; 419 if (0xFF == efuse_data) 420 return bResult; /* nothing damaged. */ 421 /* call rescue procedure */ 422 if (!fix_header(padapter, efuse_data, efuse_addr)) 423 return false; /* rescue fail */ 424 425 if (++repeat_times > _REPEAT_THRESHOLD_) /* fail */ 426 break; 427 /* otherwise, take another risk... */ 428 } 429 return bResult; 430} 431 432u8 r8712_efuse_access(struct _adapter *padapter, u8 bRead, u16 start_addr, 433 u16 cnts, u8 *data) 434{ 435 int i; 436 u8 res = true; 437 438 if (start_addr > EFUSE_MAX_SIZE) 439 return false; 440 if ((bRead == false) && ((start_addr + cnts) > 441 efuse_available_max_size)) 442 return false; 443 if ((false == bRead) && (r8712_efuse_reg_init(padapter) == false)) 444 return false; 445 /* -----------------e-fuse one byte read / write ---------------------*/ 446 for (i = 0; i < cnts; i++) { 447 if ((start_addr + i) > EFUSE_MAX_SIZE) { 448 res = false; 449 break; 450 } 451 res = efuse_one_byte_rw(padapter, bRead, start_addr + i, 452 data + i); 453 if ((false == bRead) && (false == res)) 454 break; 455 } 456 if (false == bRead) 457 r8712_efuse_reg_uninit(padapter); 458 return res; 459} 460 461u8 r8712_efuse_map_read(struct _adapter *padapter, u16 addr, u16 cnts, u8 *data) 462{ 463 u8 offset, ret = true; 464 u8 pktdata[PGPKT_DATA_SIZE]; 465 int i, idx; 466 467 if ((addr + cnts) > EFUSE_MAP_MAX_SIZE) 468 return false; 469 if ((efuse_is_empty(padapter, &offset) == true) && (offset == 470 true)) { 471 for (i = 0; i < cnts; i++) 472 data[i] = 0xFF; 473 return ret; 474 } 475 offset = (addr >> 3) & 0xF; 476 ret = r8712_efuse_pg_packet_read(padapter, offset, pktdata); 477 i = addr & 0x7; /* pktdata index */ 478 idx = 0; /* data index */ 479 480 do { 481 for (; i < PGPKT_DATA_SIZE; i++) { 482 data[idx++] = pktdata[i]; 483 if (idx == cnts) 484 return ret; 485 } 486 offset++; 487 if (!r8712_efuse_pg_packet_read(padapter, offset, pktdata)) 488 ret = false; 489 i = 0; 490 } while (1); 491 return ret; 492} 493 494u8 r8712_efuse_map_write(struct _adapter *padapter, u16 addr, u16 cnts, 495 u8 *data) 496{ 497 u8 offset, word_en, empty; 498 u8 pktdata[PGPKT_DATA_SIZE], newdata[PGPKT_DATA_SIZE]; 499 int i, j, idx; 500 501 if ((addr + cnts) > EFUSE_MAP_MAX_SIZE) 502 return false; 503 /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */ 504 empty = r8712_read8(padapter, EFUSE_CLK_CTRL); 505 if (empty != 0x03) 506 return false; 507 if (efuse_is_empty(padapter, &empty) == true) { 508 if (true == empty) 509 memset(pktdata, 0xFF, PGPKT_DATA_SIZE); 510 } else 511 return false; 512 offset = (addr >> 3) & 0xF; 513 if (empty == false) 514 if (!r8712_efuse_pg_packet_read(padapter, offset, pktdata)) 515 return false; 516 word_en = 0xF; 517 memset(newdata, 0xFF, PGPKT_DATA_SIZE); 518 i = addr & 0x7; /* pktdata index */ 519 j = 0; /* newdata index */ 520 idx = 0; /* data index */ 521 522 if (i & 0x1) { 523 /* odd start */ 524 if (data[idx] != pktdata[i]) { 525 word_en &= ~BIT(i >> 1); 526 newdata[j++] = pktdata[i - 1]; 527 newdata[j++] = data[idx]; 528 } 529 i++; 530 idx++; 531 } 532 do { 533 for (; i < PGPKT_DATA_SIZE; i += 2) { 534 if ((cnts - idx) == 1) { 535 if (data[idx] != pktdata[i]) { 536 word_en &= ~BIT(i >> 1); 537 newdata[j++] = data[idx]; 538 newdata[j++] = pktdata[1 + 1]; 539 } 540 idx++; 541 break; 542 } 543 544 if ((data[idx] != pktdata[i]) || (data[idx+1] != 545 pktdata[i+1])) { 546 word_en &= ~BIT(i >> 1); 547 newdata[j++] = data[idx]; 548 newdata[j++] = data[idx + 1]; 549 } 550 idx += 2; 551 552 if (idx == cnts) 553 break; 554 } 555 556 if (word_en != 0xF) 557 if (r8712_efuse_pg_packet_write(padapter, offset, 558 word_en, newdata) == false) 559 return false; 560 if (idx == cnts) 561 break; 562 offset++; 563 if (empty == false) 564 if (!r8712_efuse_pg_packet_read(padapter, offset, 565 pktdata)) 566 return false; 567 i = 0; 568 j = 0; 569 word_en = 0xF; 570 memset(newdata, 0xFF, PGPKT_DATA_SIZE); 571 } while (1); 572 573 return true; 574} 575