linux/drivers/staging/rtl8712/rtl8712_efuse.c
<<
>>
Prefs
   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                                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
 355u8 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
 424u8 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
 453u8 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
 485u8 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