aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/rtl8712/rtl8712_efuse.c
diff options
context:
space:
mode:
authorLarry Finger <Larry.Finger@lwfinger.net>2010-08-20 10:15:30 -0500
committerLarry Finger <Larry.Finger@lwfinger.net>2010-08-20 10:15:30 -0500
commit2865d42c78a9121caad52cb02d1fbb7f5cdbc4ef (patch)
tree430b79f753b0e1cec6379b9a4208a716c914ac65 /drivers/staging/rtl8712/rtl8712_efuse.c
parent763008c4357b73c8d18396dfd8d79dc58fa3f99d (diff)
staging: r8712u: Add the new driver to the mainline kernel
This code is for a completely new version of the Realtek 8192 USB devices such as the D-Link DWA-130. The Realtek code, which was originally for Linux, Windows XP and Windows CE, has been stripped of all code not needed for Linux. In addition, only one additional configuration variable, which enables AP mode, remains. Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net> Signed-off-by: Florian Schilhabel <florian.c.schilhabel@googlemail.com> Tested-by: Frederic Leroy <fredo@starox.org>
Diffstat (limited to 'drivers/staging/rtl8712/rtl8712_efuse.c')
-rw-r--r--drivers/staging/rtl8712/rtl8712_efuse.c568
1 files changed, 568 insertions, 0 deletions
diff --git a/drivers/staging/rtl8712/rtl8712_efuse.c b/drivers/staging/rtl8712/rtl8712_efuse.c
new file mode 100644
index 000000000000..9730ae1c58d5
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_efuse.c
@@ -0,0 +1,568 @@
+/*
+ * 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 (true == 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 (true == 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) == true) {
+ 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 hoffset = 0, 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) {
+ hoffset = (efuse_data >> 4) & 0x0F;
+ 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) ==
+ true) {
+ 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) ==
+ true) {
+ 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) == false) {
+ 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)
+ continue;
+ if (!(BIT(i) & pkt.word_en)) {
+ if (efuse_one_byte_read(padapter, addr,
+ &value) == true)
+ pkt.data[i*2] = value;
+ else
+ return false;
+ if (efuse_one_byte_read(padapter, addr + 1,
+ &value) == true)
+ 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)
+ continue;
+ 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) == false)
+ 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) == false)
+ 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) == false) {
+ 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) == false)
+ bResult = false;
+ else if (*(data+i) != efuse_data) /* fail */
+ bResult = false;
+ }
+ break;
+ } else { /* write header fail */
+ bResult = false;
+ if (0xFF == efuse_data)
+ return bResult; /* not thing damaged. */
+ /* call rescue procedure */
+ if (fix_header(padapter, efuse_data, efuse_addr) ==
+ false)
+ 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 == false) && ((start_addr + cnts) >
+ efuse_available_max_size))
+ return false;
+ if ((false == bRead) && (r8712_efuse_reg_init(padapter) == false))
+ 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 ((false == bRead) && (false == res))
+ break;
+ }
+ if (false == 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) == true) && (offset ==
+ true)) {
+ 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) == true) {
+ if (true == empty)
+ memset(pktdata, 0xFF, PGPKT_DATA_SIZE);
+ } else
+ return false;
+ offset = (addr >> 3) & 0xF;
+ if (empty == false)
+ 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;
+ } else {
+ 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) == false)
+ return false;
+ if (idx == cnts)
+ break;
+ offset++;
+ if (empty == false)
+ 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;
+}