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 (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