Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * rtl8712_efuse.c
0004  *
0005  * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
0006  * Linux device driver for RTL8192SU
0007  *
0008  * Modifications for inclusion into the Linux staging tree are
0009  * Copyright(c) 2010 Larry Finger. All rights reserved.
0010  *
0011  * Contact information:
0012  * WLAN FAE <wlanfae@realtek.com>.
0013  * Larry Finger <Larry.Finger@lwfinger.net>
0014  *
0015  ******************************************************************************/
0016 
0017 #define _RTL8712_EFUSE_C_
0018 
0019 #include "osdep_service.h"
0020 #include "drv_types.h"
0021 #include "rtl8712_efuse.h"
0022 
0023 /* reserve 3 bytes for HW stop read */
0024 static int efuse_available_max_size = EFUSE_MAX_SIZE - 3 /*0x1FD*/;
0025 
0026 static void efuse_reg_ctrl(struct _adapter *adapter, u8 bPowerOn)
0027 {
0028     u8 tmpu8 = 0;
0029 
0030     if (bPowerOn) {
0031         /* -----------------e-fuse pwr & clk reg ctrl ---------------
0032          * Enable LDOE25 Macro Block
0033          */
0034         tmpu8 = r8712_read8(adapter, EFUSE_TEST + 3);
0035         tmpu8 |= 0x80;
0036         r8712_write8(adapter, EFUSE_TEST + 3, tmpu8);
0037         msleep(20); /* for some platform , need some delay time */
0038         /* Change Efuse Clock for write action to 40MHZ */
0039         r8712_write8(adapter, EFUSE_CLK_CTRL, 0x03);
0040         msleep(20); /* for some platform , need some delay time */
0041     } else {
0042         /* -----------------e-fuse pwr & clk reg ctrl -----------------
0043          * Disable LDOE25 Macro Block
0044          */
0045         tmpu8 = r8712_read8(adapter, EFUSE_TEST + 3);
0046         tmpu8 &= 0x7F;
0047         r8712_write8(adapter, EFUSE_TEST + 3, tmpu8);
0048         /* Change Efuse Clock for write action to 500K */
0049         r8712_write8(adapter, EFUSE_CLK_CTRL, 0x02);
0050     }
0051 }
0052 
0053 /*
0054  * Before write E-Fuse, this function must be called.
0055  */
0056 u8 r8712_efuse_reg_init(struct _adapter *adapter)
0057 {
0058     return true;
0059 }
0060 
0061 void r8712_efuse_reg_uninit(struct _adapter *adapter)
0062 {
0063     efuse_reg_ctrl(adapter, false);
0064 }
0065 
0066 static u8 efuse_one_byte_read(struct _adapter *adapter, u16 addr, u8 *data)
0067 {
0068     u8 tmpidx = 0, bResult;
0069 
0070     /* -----------------e-fuse reg ctrl --------------------------------- */
0071     r8712_write8(adapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
0072     r8712_write8(adapter, EFUSE_CTRL + 2, ((u8)((addr >> 8) & 0x03)) |
0073            (r8712_read8(adapter, EFUSE_CTRL + 2) & 0xFC));
0074     r8712_write8(adapter, EFUSE_CTRL + 3, 0x72); /* read cmd */
0075     /* wait for complete */
0076     while (!(0x80 & r8712_read8(adapter, EFUSE_CTRL + 3)) &&
0077            (tmpidx < 100))
0078         tmpidx++;
0079     if (tmpidx < 100) {
0080         *data = r8712_read8(adapter, EFUSE_CTRL);
0081         bResult = true;
0082     } else {
0083         *data = 0xff;
0084         bResult = false;
0085     }
0086     return bResult;
0087 }
0088 
0089 static u8 efuse_one_byte_write(struct _adapter *adapter, u16 addr, u8 data)
0090 {
0091     u8 tmpidx = 0, bResult;
0092 
0093     /* -----------------e-fuse reg ctrl -------------------------------- */
0094     r8712_write8(adapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
0095     r8712_write8(adapter, EFUSE_CTRL + 2, ((u8)((addr >> 8) & 0x03)) |
0096            (r8712_read8(adapter, EFUSE_CTRL + 2) & 0xFC));
0097     r8712_write8(adapter, EFUSE_CTRL, data); /* data */
0098     r8712_write8(adapter, EFUSE_CTRL + 3, 0xF2); /* write cmd */
0099     /* wait for complete */
0100     while ((0x80 &  r8712_read8(adapter, EFUSE_CTRL + 3)) &&
0101            (tmpidx < 100))
0102         tmpidx++;
0103     if (tmpidx < 100)
0104         bResult = true;
0105     else
0106         bResult = false;
0107     return bResult;
0108 }
0109 
0110 static u8 efuse_one_byte_rw(struct _adapter *adapter, u8 bRead, u16 addr,
0111                 u8 *data)
0112 {
0113     u8 tmpidx = 0, tmpv8 = 0, bResult;
0114 
0115     /* -----------------e-fuse reg ctrl --------------------------------- */
0116     r8712_write8(adapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
0117     tmpv8 = ((u8)((addr >> 8) & 0x03)) |
0118          (r8712_read8(adapter, EFUSE_CTRL + 2) & 0xFC);
0119     r8712_write8(adapter, EFUSE_CTRL + 2, tmpv8);
0120     if (bRead) {
0121         r8712_write8(adapter, EFUSE_CTRL + 3,  0x72); /* read cmd */
0122         while (!(0x80 & r8712_read8(adapter, EFUSE_CTRL + 3)) &&
0123                (tmpidx < 100))
0124             tmpidx++;
0125         if (tmpidx < 100) {
0126             *data = r8712_read8(adapter, EFUSE_CTRL);
0127             bResult = true;
0128         } else {
0129             *data = 0;
0130             bResult = false;
0131         }
0132     } else {
0133         r8712_write8(adapter, EFUSE_CTRL, *data); /* data */
0134         r8712_write8(adapter, EFUSE_CTRL + 3, 0xF2); /* write cmd */
0135         while ((0x80 & r8712_read8(adapter, EFUSE_CTRL + 3)) &&
0136                (tmpidx < 100))
0137             tmpidx++;
0138         if (tmpidx < 100)
0139             bResult = true;
0140         else
0141             bResult = false;
0142     }
0143     return bResult;
0144 }
0145 
0146 static u8 efuse_is_empty(struct _adapter *adapter, u8 *empty)
0147 {
0148     u8 value, ret = true;
0149 
0150     /* read one byte to check if E-Fuse is empty */
0151     if (efuse_one_byte_rw(adapter, true, 0, &value)) {
0152         if (value == 0xFF)
0153             *empty = true;
0154         else
0155             *empty = false;
0156     } else {
0157         ret = false;
0158     }
0159     return ret;
0160 }
0161 
0162 void r8712_efuse_change_max_size(struct _adapter *adapter)
0163 {
0164     u16 pre_pg_data_saddr = 0x1FB;
0165     u16 i;
0166     u16 pre_pg_data_size = 5;
0167     u8 pre_pg_data[5];
0168 
0169     for (i = 0; i < pre_pg_data_size; i++)
0170         efuse_one_byte_read(adapter, pre_pg_data_saddr + i,
0171                     &pre_pg_data[i]);
0172     if ((pre_pg_data[0] == 0x03) && (pre_pg_data[1] == 0x00) &&
0173         (pre_pg_data[2] == 0x00) && (pre_pg_data[3] == 0x00) &&
0174         (pre_pg_data[4] == 0x0C))
0175         efuse_available_max_size -= pre_pg_data_size;
0176 }
0177 
0178 int r8712_efuse_get_max_size(struct _adapter *adapter)
0179 {
0180     return  efuse_available_max_size;
0181 }
0182 
0183 static u8 calculate_word_cnts(const u8 word_en)
0184 {
0185     u8 word_cnts = 0;
0186     u8 word_idx;
0187 
0188     for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++)
0189         if (!(word_en & BIT(word_idx)))
0190             word_cnts++; /* 0 : write enable */
0191     return word_cnts;
0192 }
0193 
0194 static void pgpacket_copy_data(const u8 word_en, const u8 *sourdata,
0195                    u8 *targetdata)
0196 {
0197     u8 tmpindex = 0;
0198     u8 word_idx, byte_idx;
0199 
0200     for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++) {
0201         if (!(word_en & BIT(word_idx))) {
0202             byte_idx = word_idx * 2;
0203             targetdata[byte_idx] = sourdata[tmpindex++];
0204             targetdata[byte_idx + 1] = sourdata[tmpindex++];
0205         }
0206     }
0207 }
0208 
0209 u16 r8712_efuse_get_current_size(struct _adapter *adapter)
0210 {
0211     int bContinual = true;
0212     u16 efuse_addr = 0;
0213     u8 hworden = 0;
0214     u8 efuse_data, word_cnts = 0;
0215 
0216     while (bContinual && efuse_one_byte_read(adapter, efuse_addr,
0217            &efuse_data) && (efuse_addr < efuse_available_max_size)) {
0218         if (efuse_data != 0xFF) {
0219             hworden =  efuse_data & 0x0F;
0220             word_cnts = calculate_word_cnts(hworden);
0221             /* read next header */
0222             efuse_addr = efuse_addr + (word_cnts * 2) + 1;
0223         } else {
0224             bContinual = false;
0225         }
0226     }
0227     return efuse_addr;
0228 }
0229 
0230 u8 r8712_efuse_pg_packet_read(struct _adapter *adapter, u8 offset, u8 *data)
0231 {
0232     u8 hoffset = 0, hworden = 0, word_cnts = 0;
0233     u16 efuse_addr = 0;
0234     u8 efuse_data;
0235     u8 tmpidx = 0;
0236     u8 tmpdata[PGPKT_DATA_SIZE];
0237     u8 ret = true;
0238 
0239     if (!data)
0240         return false;
0241     if (offset > 0x0f)
0242         return false;
0243     memset(data, 0xFF, sizeof(u8) * PGPKT_DATA_SIZE);
0244     while (efuse_addr < efuse_available_max_size) {
0245         if (efuse_one_byte_read(adapter, efuse_addr, &efuse_data)) {
0246             if (efuse_data == 0xFF)
0247                 break;
0248             hoffset = (efuse_data >> 4) & 0x0F;
0249             hworden =  efuse_data & 0x0F;
0250             word_cnts = calculate_word_cnts(hworden);
0251             if (hoffset == offset) {
0252                 memset(tmpdata, 0xFF, PGPKT_DATA_SIZE);
0253                 for (tmpidx = 0; tmpidx < word_cnts * 2;
0254                      tmpidx++) {
0255                     if (efuse_one_byte_read(adapter,
0256                         efuse_addr + 1 + tmpidx,
0257                         &efuse_data)) {
0258                         tmpdata[tmpidx] = efuse_data;
0259                     } else {
0260                         ret = false;
0261                     }
0262                 }
0263                 pgpacket_copy_data(hworden, tmpdata, data);
0264             }
0265             efuse_addr += 1 + (word_cnts * 2);
0266         } else {
0267             ret = false;
0268             break;
0269         }
0270     }
0271     return ret;
0272 }
0273 
0274 static u8 fix_header(struct _adapter *adapter, u8 header, u16 header_addr)
0275 {
0276     struct PGPKT_STRUCT pkt;
0277     u8 offset, word_en, value;
0278     u16 addr;
0279     int i;
0280     u8 ret = true;
0281 
0282     pkt.offset = GET_EFUSE_OFFSET(header);
0283     pkt.word_en = GET_EFUSE_WORD_EN(header);
0284     addr = header_addr + 1 + calculate_word_cnts(pkt.word_en) * 2;
0285     if (addr > efuse_available_max_size)
0286         return false;
0287     /* retrieve original data */
0288     addr = 0;
0289     while (addr < header_addr) {
0290         if (!efuse_one_byte_read(adapter, addr++, &value)) {
0291             ret = false;
0292             break;
0293         }
0294         offset = GET_EFUSE_OFFSET(value);
0295         word_en = GET_EFUSE_WORD_EN(value);
0296         if (pkt.offset != offset) {
0297             addr += calculate_word_cnts(word_en) * 2;
0298             continue;
0299         }
0300         for (i = 0; i < PGPKG_MAX_WORDS; i++) {
0301             if (!(BIT(i) & word_en))
0302                 continue;
0303             if (BIT(i) & pkt.word_en) {
0304                 if (efuse_one_byte_read(adapter,
0305                             addr,
0306                             &value))
0307                     pkt.data[i * 2] = value;
0308                 else
0309                     return false;
0310                 if (efuse_one_byte_read(adapter,
0311                             addr + 1,
0312                             &value))
0313                     pkt.data[i * 2 + 1] = value;
0314                 else
0315                     return false;
0316             }
0317             addr += 2;
0318         }
0319     }
0320     if (addr != header_addr)
0321         return false;
0322     addr++;
0323     /* fill original data */
0324     for (i = 0; i < PGPKG_MAX_WORDS; i++) {
0325         if (BIT(i) & pkt.word_en) {
0326             efuse_one_byte_write(adapter, addr, pkt.data[i * 2]);
0327             efuse_one_byte_write(adapter, addr + 1,
0328                          pkt.data[i * 2 + 1]);
0329             /* additional check */
0330             if (!efuse_one_byte_read(adapter, addr, &value)) {
0331                 ret = false;
0332             } else if (pkt.data[i * 2] != value) {
0333                 ret = false;
0334                 if (value == 0xFF) /* write again */
0335                     efuse_one_byte_write(adapter, addr,
0336                                  pkt.data[i * 2]);
0337             }
0338             if (!efuse_one_byte_read(adapter, addr + 1, &value)) {
0339                 ret = false;
0340             } else if (pkt.data[i * 2 + 1] != value) {
0341                 ret = false;
0342                 if (value == 0xFF) /* write again */
0343                     efuse_one_byte_write(adapter, addr + 1,
0344                                  pkt.data[i * 2 +
0345                                       1]);
0346             }
0347         }
0348         addr += 2;
0349     }
0350     return ret;
0351 }
0352 
0353 u8 r8712_efuse_pg_packet_write(struct _adapter *adapter, const u8 offset,
0354                    const u8 word_en, const u8 *data)
0355 {
0356     u8 pg_header = 0;
0357     u16 efuse_addr = 0, curr_size = 0;
0358     u8 efuse_data, target_word_cnts = 0;
0359     int repeat_times;
0360     int sub_repeat;
0361     u8 bResult = true;
0362 
0363     /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
0364     efuse_data = r8712_read8(adapter, EFUSE_CLK_CTRL);
0365     if (efuse_data != 0x03)
0366         return false;
0367     pg_header = MAKE_EFUSE_HEADER(offset, word_en);
0368     target_word_cnts = calculate_word_cnts(word_en);
0369     repeat_times = 0;
0370     efuse_addr = 0;
0371     while (efuse_addr < efuse_available_max_size) {
0372         curr_size = r8712_efuse_get_current_size(adapter);
0373         if ((curr_size + 1 + target_word_cnts * 2) >
0374              efuse_available_max_size)
0375             return false; /*target_word_cnts + pg header(1 byte)*/
0376         efuse_addr = curr_size; /* current size is also the last addr*/
0377         efuse_one_byte_write(adapter, efuse_addr, pg_header); /*hdr*/
0378         sub_repeat = 0;
0379         /* check if what we read is what we write */
0380         while (!efuse_one_byte_read(adapter, efuse_addr,
0381                         &efuse_data)) {
0382             if (++sub_repeat > _REPEAT_THRESHOLD_) {
0383                 bResult = false; /* continue to blind write */
0384                 break; /* continue to blind write */
0385             }
0386         }
0387         if ((sub_repeat > _REPEAT_THRESHOLD_) ||
0388             (pg_header == efuse_data)) {
0389             /* write header ok OR can't check header(creep) */
0390             u8 i;
0391 
0392             /* go to next address */
0393             efuse_addr++;
0394             for (i = 0; i < target_word_cnts * 2; i++) {
0395                 efuse_one_byte_write(adapter,
0396                              efuse_addr + i,
0397                              *(data + i));
0398                 if (!efuse_one_byte_read(adapter,
0399                              efuse_addr + i,
0400                              &efuse_data))
0401                     bResult = false;
0402                 else if (*(data + i) != efuse_data) /* fail */
0403                     bResult = false;
0404             }
0405             break;
0406         }
0407         /* write header fail */
0408         bResult = false;
0409         if (efuse_data == 0xFF)
0410             return bResult; /* nothing damaged. */
0411         /* call rescue procedure */
0412         if (!fix_header(adapter, efuse_data, efuse_addr))
0413             return false; /* rescue fail */
0414 
0415         if (++repeat_times > _REPEAT_THRESHOLD_) /* fail */
0416             break;
0417         /* otherwise, take another risk... */
0418     }
0419     return bResult;
0420 }
0421 
0422 u8 r8712_efuse_access(struct _adapter *adapter, u8 bRead, u16 start_addr,
0423               u16 cnts, u8 *data)
0424 {
0425     int i;
0426     u8 res = true;
0427 
0428     if (start_addr > EFUSE_MAX_SIZE)
0429         return false;
0430     if (!bRead && ((start_addr + cnts) >
0431        efuse_available_max_size))
0432         return false;
0433     if (!bRead && !r8712_efuse_reg_init(adapter))
0434         return false;
0435     /* -----------------e-fuse one byte read / write ---------------------*/
0436     for (i = 0; i < cnts; i++) {
0437         if ((start_addr + i) > EFUSE_MAX_SIZE) {
0438             res = false;
0439             break;
0440         }
0441         res = efuse_one_byte_rw(adapter, bRead, start_addr + i,
0442                     data + i);
0443         if (!bRead && !res)
0444             break;
0445     }
0446     if (!bRead)
0447         r8712_efuse_reg_uninit(adapter);
0448     return res;
0449 }
0450 
0451 u8 r8712_efuse_map_read(struct _adapter *adapter, u16 addr, u16 cnts, u8 *data)
0452 {
0453     u8 offset, ret = true;
0454     u8 pktdata[PGPKT_DATA_SIZE];
0455     int i, idx;
0456 
0457     if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
0458         return false;
0459     if (efuse_is_empty(adapter, &offset) && offset) {
0460         for (i = 0; i < cnts; i++)
0461             data[i] = 0xFF;
0462         return ret;
0463     }
0464     offset = (addr >> 3) & 0xF;
0465     ret = r8712_efuse_pg_packet_read(adapter, offset, pktdata);
0466     i = addr & 0x7; /* pktdata index */
0467     idx = 0;    /* data index */
0468 
0469     do {
0470         for (; i < PGPKT_DATA_SIZE; i++) {
0471             data[idx++] = pktdata[i];
0472             if (idx == cnts)
0473                 return ret;
0474         }
0475         offset++;
0476         if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata))
0477             ret = false;
0478         i = 0;
0479     } while (1);
0480     return ret;
0481 }
0482 
0483 u8 r8712_efuse_map_write(struct _adapter *adapter, u16 addr, u16 cnts,
0484              u8 *data)
0485 {
0486     u8 offset, word_en, empty;
0487     u8 pktdata[PGPKT_DATA_SIZE], newdata[PGPKT_DATA_SIZE];
0488     int i, j, idx;
0489 
0490     if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
0491         return false;
0492     /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
0493     empty = r8712_read8(adapter, EFUSE_CLK_CTRL);
0494     if (empty != 0x03)
0495         return false;
0496     if (efuse_is_empty(adapter, &empty)) {
0497         if (empty)
0498             memset(pktdata, 0xFF, PGPKT_DATA_SIZE);
0499     } else {
0500         return false;
0501     }
0502     offset = (addr >> 3) & 0xF;
0503     if (!empty)
0504         if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata))
0505             return false;
0506     word_en = 0xF;
0507     memset(newdata, 0xFF, PGPKT_DATA_SIZE);
0508     i = addr & 0x7; /* pktdata index */
0509     j = 0;      /* newdata index */
0510     idx = 0;    /* data index */
0511 
0512     if (i & 0x1) {
0513         /*  odd start */
0514         if (data[idx] != pktdata[i]) {
0515             word_en &= ~BIT(i >> 1);
0516             newdata[j++] = pktdata[i - 1];
0517             newdata[j++] = data[idx];
0518         }
0519         i++;
0520         idx++;
0521     }
0522     do {
0523         for (; i < PGPKT_DATA_SIZE; i += 2) {
0524             if ((cnts - idx) == 1) {
0525                 if (data[idx] != pktdata[i]) {
0526                     word_en &= ~BIT(i >> 1);
0527                     newdata[j++] = data[idx];
0528                     newdata[j++] = pktdata[1 + 1];
0529                 }
0530                 idx++;
0531                 break;
0532             }
0533 
0534             if ((data[idx] != pktdata[i]) || (data[idx + 1] !=
0535                  pktdata[i + 1])) {
0536                 word_en &= ~BIT(i >> 1);
0537                 newdata[j++] = data[idx];
0538                 newdata[j++] = data[idx + 1];
0539             }
0540             idx += 2;
0541 
0542             if (idx == cnts)
0543                 break;
0544         }
0545 
0546         if (word_en != 0xF)
0547             if (!r8712_efuse_pg_packet_write(adapter, offset,
0548                              word_en, newdata))
0549                 return false;
0550         if (idx == cnts)
0551             break;
0552         offset++;
0553         if (!empty)
0554             if (!r8712_efuse_pg_packet_read(adapter, offset,
0555                             pktdata))
0556                 return false;
0557         i = 0;
0558         j = 0;
0559         word_en = 0xF;
0560         memset(newdata, 0xFF, PGPKT_DATA_SIZE);
0561     } while (1);
0562 
0563     return true;
0564 }