123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- /*
- * rtl8712_efuse.c
- *
- * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
- * Linux device driver for RTL8192SU
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
- *
- * Modifications for inclusion into the Linux staging tree are
- * Copyright(c) 2010 Larry Finger. All rights reserved.
- *
- * Contact information:
- * WLAN FAE <wlanfae@realtek.com>.
- * Larry Finger <Larry.Finger@lwfinger.net>
- *
- ******************************************************************************/
- #define _RTL8712_EFUSE_C_
- #include "osdep_service.h"
- #include "drv_types.h"
- #include "rtl8712_efuse.h"
- /* reserve 3 bytes for HW stop read */
- static int efuse_available_max_size = EFUSE_MAX_SIZE - 3 /*0x1FD*/;
- static void efuse_reg_ctrl(struct _adapter *padapter, u8 bPowerOn)
- {
- u8 tmpu8 = 0;
- if (bPowerOn) {
- /* -----------------e-fuse pwr & clk reg ctrl ---------------
- * Enable LDOE25 Macro Block
- */
- tmpu8 = r8712_read8(padapter, EFUSE_TEST + 3);
- tmpu8 |= 0x80;
- r8712_write8(padapter, EFUSE_TEST + 3, tmpu8);
- msleep(20); /* for some platform , need some delay time */
- /* Change Efuse Clock for write action to 40MHZ */
- r8712_write8(padapter, EFUSE_CLK_CTRL, 0x03);
- msleep(20); /* for some platform , need some delay time */
- } else {
- /* -----------------e-fuse pwr & clk reg ctrl -----------------
- * Disable LDOE25 Macro Block
- */
- tmpu8 = r8712_read8(padapter, EFUSE_TEST + 3);
- tmpu8 &= 0x7F;
- r8712_write8(padapter, EFUSE_TEST + 3, tmpu8);
- /* Change Efuse Clock for write action to 500K */
- r8712_write8(padapter, EFUSE_CLK_CTRL, 0x02);
- }
- }
- /*
- * Before write E-Fuse, this function must be called.
- */
- u8 r8712_efuse_reg_init(struct _adapter *padapter)
- {
- return true;
- }
- void r8712_efuse_reg_uninit(struct _adapter *padapter)
- {
- efuse_reg_ctrl(padapter, false);
- }
- static u8 efuse_one_byte_read(struct _adapter *padapter, u16 addr, u8 *data)
- {
- u8 tmpidx = 0, bResult;
- /* -----------------e-fuse reg ctrl --------------------------------- */
- r8712_write8(padapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
- r8712_write8(padapter, EFUSE_CTRL + 2, ((u8)((addr >> 8) & 0x03)) |
- (r8712_read8(padapter, EFUSE_CTRL + 2) & 0xFC));
- r8712_write8(padapter, EFUSE_CTRL + 3, 0x72); /* read cmd */
- /* wait for complete */
- while (!(0x80 & r8712_read8(padapter, EFUSE_CTRL + 3)) &&
- (tmpidx < 100))
- tmpidx++;
- if (tmpidx < 100) {
- *data = r8712_read8(padapter, EFUSE_CTRL);
- bResult = true;
- } else {
- *data = 0xff;
- bResult = false;
- }
- return bResult;
- }
- static u8 efuse_one_byte_write(struct _adapter *padapter, u16 addr, u8 data)
- {
- u8 tmpidx = 0, bResult;
- /* -----------------e-fuse reg ctrl -------------------------------- */
- r8712_write8(padapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
- r8712_write8(padapter, EFUSE_CTRL + 2, ((u8)((addr >> 8) & 0x03)) |
- (r8712_read8(padapter, EFUSE_CTRL + 2) & 0xFC));
- r8712_write8(padapter, EFUSE_CTRL, data); /* data */
- r8712_write8(padapter, EFUSE_CTRL + 3, 0xF2); /* write cmd */
- /* wait for complete */
- while ((0x80 & r8712_read8(padapter, EFUSE_CTRL + 3)) &&
- (tmpidx < 100))
- tmpidx++;
- if (tmpidx < 100)
- bResult = true;
- else
- bResult = false;
- return bResult;
- }
- static u8 efuse_one_byte_rw(struct _adapter *padapter, u8 bRead, u16 addr,
- u8 *data)
- {
- u8 tmpidx = 0, tmpv8 = 0, bResult;
- /* -----------------e-fuse reg ctrl --------------------------------- */
- r8712_write8(padapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
- tmpv8 = ((u8)((addr >> 8) & 0x03)) |
- (r8712_read8(padapter, EFUSE_CTRL + 2) & 0xFC);
- r8712_write8(padapter, EFUSE_CTRL + 2, tmpv8);
- if (bRead) {
- r8712_write8(padapter, EFUSE_CTRL + 3, 0x72); /* read cmd */
- while (!(0x80 & r8712_read8(padapter, EFUSE_CTRL + 3)) &&
- (tmpidx < 100))
- tmpidx++;
- if (tmpidx < 100) {
- *data = r8712_read8(padapter, EFUSE_CTRL);
- bResult = true;
- } else {
- *data = 0;
- bResult = false;
- }
- } else {
- r8712_write8(padapter, EFUSE_CTRL, *data); /* data */
- r8712_write8(padapter, EFUSE_CTRL + 3, 0xF2); /* write cmd */
- while ((0x80 & r8712_read8(padapter, EFUSE_CTRL + 3)) &&
- (tmpidx < 100))
- tmpidx++;
- if (tmpidx < 100)
- bResult = true;
- else
- bResult = false;
- }
- return bResult;
- }
- static u8 efuse_is_empty(struct _adapter *padapter, u8 *empty)
- {
- u8 value, ret = true;
- /* read one byte to check if E-Fuse is empty */
- if (efuse_one_byte_rw(padapter, true, 0, &value)) {
- if (0xFF == value)
- *empty = true;
- else
- *empty = false;
- } else {
- ret = false;
- }
- return ret;
- }
- void r8712_efuse_change_max_size(struct _adapter *padapter)
- {
- u16 pre_pg_data_saddr = 0x1FB;
- u16 i;
- u16 pre_pg_data_size = 5;
- u8 pre_pg_data[5];
- for (i = 0; i < pre_pg_data_size; i++)
- efuse_one_byte_read(padapter, pre_pg_data_saddr + i,
- &pre_pg_data[i]);
- if ((pre_pg_data[0] == 0x03) && (pre_pg_data[1] == 0x00) &&
- (pre_pg_data[2] == 0x00) && (pre_pg_data[3] == 0x00) &&
- (pre_pg_data[4] == 0x0C))
- efuse_available_max_size -= pre_pg_data_size;
- }
- int r8712_efuse_get_max_size(struct _adapter *padapter)
- {
- return efuse_available_max_size;
- }
- static u8 calculate_word_cnts(const u8 word_en)
- {
- u8 word_cnts = 0;
- u8 word_idx;
- for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++)
- if (!(word_en & BIT(word_idx)))
- word_cnts++; /* 0 : write enable */
- return word_cnts;
- }
- static void pgpacket_copy_data(const u8 word_en, const u8 *sourdata,
- u8 *targetdata)
- {
- u8 tmpindex = 0;
- u8 word_idx, byte_idx;
- for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++) {
- if (!(word_en & BIT(word_idx))) {
- byte_idx = word_idx * 2;
- targetdata[byte_idx] = sourdata[tmpindex++];
- targetdata[byte_idx + 1] = sourdata[tmpindex++];
- }
- }
- }
- u16 r8712_efuse_get_current_size(struct _adapter *padapter)
- {
- int bContinual = true;
- u16 efuse_addr = 0;
- u8 hworden = 0;
- u8 efuse_data, word_cnts = 0;
- while (bContinual && efuse_one_byte_read(padapter, efuse_addr,
- &efuse_data) && (efuse_addr < efuse_available_max_size)) {
- if (efuse_data != 0xFF) {
- hworden = efuse_data & 0x0F;
- word_cnts = calculate_word_cnts(hworden);
- /* read next header */
- efuse_addr = efuse_addr + (word_cnts * 2) + 1;
- } else {
- bContinual = false;
- }
- }
- return efuse_addr;
- }
- u8 r8712_efuse_pg_packet_read(struct _adapter *padapter, u8 offset, u8 *data)
- {
- u8 hoffset = 0, hworden = 0, word_cnts = 0;
- u16 efuse_addr = 0;
- u8 efuse_data;
- u8 tmpidx = 0;
- u8 tmpdata[PGPKT_DATA_SIZE];
- u8 ret = true;
- if (data == NULL)
- return false;
- if (offset > 0x0f)
- return false;
- memset(data, 0xFF, sizeof(u8) * PGPKT_DATA_SIZE);
- while (efuse_addr < efuse_available_max_size) {
- if (efuse_one_byte_read(padapter, efuse_addr, &efuse_data)) {
- if (efuse_data == 0xFF)
- break;
- hoffset = (efuse_data >> 4) & 0x0F;
- hworden = efuse_data & 0x0F;
- word_cnts = calculate_word_cnts(hworden);
- if (hoffset == offset) {
- memset(tmpdata, 0xFF, PGPKT_DATA_SIZE);
- for (tmpidx = 0; tmpidx < word_cnts * 2;
- tmpidx++) {
- if (efuse_one_byte_read(padapter,
- efuse_addr + 1 + tmpidx,
- &efuse_data)) {
- tmpdata[tmpidx] = efuse_data;
- } else {
- ret = false;
- }
- }
- pgpacket_copy_data(hworden, tmpdata, data);
- }
- efuse_addr += 1 + (word_cnts * 2);
- } else {
- ret = false;
- break;
- }
- }
- return ret;
- }
- static u8 fix_header(struct _adapter *padapter, u8 header, u16 header_addr)
- {
- struct PGPKT_STRUCT pkt;
- u8 offset, word_en, value;
- u16 addr;
- int i;
- u8 ret = true;
- pkt.offset = GET_EFUSE_OFFSET(header);
- pkt.word_en = GET_EFUSE_WORD_EN(header);
- addr = header_addr + 1 + calculate_word_cnts(pkt.word_en) * 2;
- if (addr > efuse_available_max_size)
- return false;
- /* retrieve original data */
- addr = 0;
- while (addr < header_addr) {
- if (!efuse_one_byte_read(padapter, addr++, &value)) {
- ret = false;
- break;
- }
- offset = GET_EFUSE_OFFSET(value);
- word_en = GET_EFUSE_WORD_EN(value);
- if (pkt.offset != offset) {
- addr += calculate_word_cnts(word_en) * 2;
- continue;
- }
- for (i = 0; i < PGPKG_MAX_WORDS; i++) {
- if (BIT(i) & word_en) {
- if (BIT(i) & pkt.word_en) {
- if (efuse_one_byte_read(
- padapter, addr,
- &value))
- pkt.data[i * 2] = value;
- else
- return false;
- if (efuse_one_byte_read(
- padapter,
- addr + 1,
- &value))
- pkt.data[i * 2 + 1] =
- value;
- else
- return false;
- }
- addr += 2;
- }
- }
- }
- if (addr != header_addr)
- return false;
- addr++;
- /* fill original data */
- for (i = 0; i < PGPKG_MAX_WORDS; i++) {
- if (BIT(i) & pkt.word_en) {
- efuse_one_byte_write(padapter, addr, pkt.data[i * 2]);
- efuse_one_byte_write(padapter, addr + 1,
- pkt.data[i * 2 + 1]);
- /* additional check */
- if (!efuse_one_byte_read(padapter, addr, &value)) {
- ret = false;
- } else if (pkt.data[i * 2] != value) {
- ret = false;
- if (0xFF == value) /* write again */
- efuse_one_byte_write(padapter, addr,
- pkt.data[i * 2]);
- }
- if (!efuse_one_byte_read(padapter, addr + 1, &value)) {
- ret = false;
- } else if (pkt.data[i * 2 + 1] != value) {
- ret = false;
- if (0xFF == value) /* write again */
- efuse_one_byte_write(padapter, addr + 1,
- pkt.data[i * 2 +
- 1]);
- }
- }
- addr += 2;
- }
- return ret;
- }
- u8 r8712_efuse_pg_packet_write(struct _adapter *padapter, const u8 offset,
- const u8 word_en, const u8 *data)
- {
- u8 pg_header = 0;
- u16 efuse_addr = 0, curr_size = 0;
- u8 efuse_data, target_word_cnts = 0;
- static int repeat_times;
- int sub_repeat;
- u8 bResult = true;
- /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
- efuse_data = r8712_read8(padapter, EFUSE_CLK_CTRL);
- if (efuse_data != 0x03)
- return false;
- pg_header = MAKE_EFUSE_HEADER(offset, word_en);
- target_word_cnts = calculate_word_cnts(word_en);
- repeat_times = 0;
- efuse_addr = 0;
- while (efuse_addr < efuse_available_max_size) {
- curr_size = r8712_efuse_get_current_size(padapter);
- if ((curr_size + 1 + target_word_cnts * 2) >
- efuse_available_max_size)
- return false; /*target_word_cnts + pg header(1 byte)*/
- efuse_addr = curr_size; /* current size is also the last addr*/
- efuse_one_byte_write(padapter, efuse_addr, pg_header); /*hdr*/
- sub_repeat = 0;
- /* check if what we read is what we write */
- while (!efuse_one_byte_read(padapter, efuse_addr,
- &efuse_data)) {
- if (++sub_repeat > _REPEAT_THRESHOLD_) {
- bResult = false; /* continue to blind write */
- break; /* continue to blind write */
- }
- }
- if ((sub_repeat > _REPEAT_THRESHOLD_) ||
- (pg_header == efuse_data)) {
- /* write header ok OR can't check header(creep) */
- u8 i;
- /* go to next address */
- efuse_addr++;
- for (i = 0; i < target_word_cnts * 2; i++) {
- efuse_one_byte_write(padapter,
- efuse_addr + i,
- *(data + i));
- if (!efuse_one_byte_read(padapter,
- efuse_addr + i,
- &efuse_data))
- bResult = false;
- else if (*(data + i) != efuse_data) /* fail */
- bResult = false;
- }
- break;
- }
- /* write header fail */
- bResult = false;
- if (0xFF == efuse_data)
- return bResult; /* nothing damaged. */
- /* call rescue procedure */
- if (!fix_header(padapter, efuse_data, efuse_addr))
- return false; /* rescue fail */
- if (++repeat_times > _REPEAT_THRESHOLD_) /* fail */
- break;
- /* otherwise, take another risk... */
- }
- return bResult;
- }
- u8 r8712_efuse_access(struct _adapter *padapter, u8 bRead, u16 start_addr,
- u16 cnts, u8 *data)
- {
- int i;
- u8 res = true;
- if (start_addr > EFUSE_MAX_SIZE)
- return false;
- if (!bRead && ((start_addr + cnts) >
- efuse_available_max_size))
- return false;
- if (!bRead && !r8712_efuse_reg_init(padapter))
- return false;
- /* -----------------e-fuse one byte read / write ---------------------*/
- for (i = 0; i < cnts; i++) {
- if ((start_addr + i) > EFUSE_MAX_SIZE) {
- res = false;
- break;
- }
- res = efuse_one_byte_rw(padapter, bRead, start_addr + i,
- data + i);
- if (!bRead && !res)
- break;
- }
- if (!bRead)
- r8712_efuse_reg_uninit(padapter);
- return res;
- }
- u8 r8712_efuse_map_read(struct _adapter *padapter, u16 addr, u16 cnts, u8 *data)
- {
- u8 offset, ret = true;
- u8 pktdata[PGPKT_DATA_SIZE];
- int i, idx;
- if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
- return false;
- if (efuse_is_empty(padapter, &offset) && offset) {
- for (i = 0; i < cnts; i++)
- data[i] = 0xFF;
- return ret;
- }
- offset = (addr >> 3) & 0xF;
- ret = r8712_efuse_pg_packet_read(padapter, offset, pktdata);
- i = addr & 0x7; /* pktdata index */
- idx = 0; /* data index */
- do {
- for (; i < PGPKT_DATA_SIZE; i++) {
- data[idx++] = pktdata[i];
- if (idx == cnts)
- return ret;
- }
- offset++;
- if (!r8712_efuse_pg_packet_read(padapter, offset, pktdata))
- ret = false;
- i = 0;
- } while (1);
- return ret;
- }
- u8 r8712_efuse_map_write(struct _adapter *padapter, u16 addr, u16 cnts,
- u8 *data)
- {
- u8 offset, word_en, empty;
- u8 pktdata[PGPKT_DATA_SIZE], newdata[PGPKT_DATA_SIZE];
- int i, j, idx;
- if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
- return false;
- /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
- empty = r8712_read8(padapter, EFUSE_CLK_CTRL);
- if (empty != 0x03)
- return false;
- if (efuse_is_empty(padapter, &empty)) {
- if (empty)
- memset(pktdata, 0xFF, PGPKT_DATA_SIZE);
- } else {
- return false;
- }
- offset = (addr >> 3) & 0xF;
- if (!empty)
- if (!r8712_efuse_pg_packet_read(padapter, offset, pktdata))
- return false;
- word_en = 0xF;
- memset(newdata, 0xFF, PGPKT_DATA_SIZE);
- i = addr & 0x7; /* pktdata index */
- j = 0; /* newdata index */
- idx = 0; /* data index */
- if (i & 0x1) {
- /* odd start */
- if (data[idx] != pktdata[i]) {
- word_en &= ~BIT(i >> 1);
- newdata[j++] = pktdata[i - 1];
- newdata[j++] = data[idx];
- }
- i++;
- idx++;
- }
- do {
- for (; i < PGPKT_DATA_SIZE; i += 2) {
- if ((cnts - idx) == 1) {
- if (data[idx] != pktdata[i]) {
- word_en &= ~BIT(i >> 1);
- newdata[j++] = data[idx];
- newdata[j++] = pktdata[1 + 1];
- }
- idx++;
- break;
- }
- if ((data[idx] != pktdata[i]) || (data[idx + 1] !=
- pktdata[i + 1])) {
- word_en &= ~BIT(i >> 1);
- newdata[j++] = data[idx];
- newdata[j++] = data[idx + 1];
- }
- idx += 2;
- if (idx == cnts)
- break;
- }
- if (word_en != 0xF)
- if (!r8712_efuse_pg_packet_write(padapter, offset,
- word_en, newdata))
- return false;
- if (idx == cnts)
- break;
- offset++;
- if (!empty)
- if (!r8712_efuse_pg_packet_read(padapter, offset,
- pktdata))
- return false;
- i = 0;
- j = 0;
- word_en = 0xF;
- memset(newdata, 0xFF, PGPKT_DATA_SIZE);
- } while (1);
- return true;
- }
|