root/drivers/staging/rtl8712/rtl8712_efuse.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. efuse_reg_ctrl
  2. r8712_efuse_reg_init
  3. r8712_efuse_reg_uninit
  4. efuse_one_byte_read
  5. efuse_one_byte_write
  6. efuse_one_byte_rw
  7. efuse_is_empty
  8. r8712_efuse_change_max_size
  9. r8712_efuse_get_max_size
  10. calculate_word_cnts
  11. pgpacket_copy_data
  12. r8712_efuse_get_current_size
  13. r8712_efuse_pg_packet_read
  14. fix_header
  15. r8712_efuse_pg_packet_write
  16. r8712_efuse_access
  17. r8712_efuse_map_read
  18. r8712_efuse_map_write

   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 }

/* [<][>][^][v][top][bottom][index][help] */