linux/drivers/staging/rtl8712/rtl8712_efuse.c
<<
>>
Prefs
   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 (value == 0xFF)
 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)
 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 (value == 0xFF) /* 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 (value == 0xFF) /* 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 (efuse_data == 0xFF)
 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