summaryrefslogtreecommitdiffstats
path: root/drivers/staging/rtl8723au/hal/rtl8723a_hal_init.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/rtl8723au/hal/rtl8723a_hal_init.c')
-rw-r--r--drivers/staging/rtl8723au/hal/rtl8723a_hal_init.c3452
1 files changed, 3452 insertions, 0 deletions
diff --git a/drivers/staging/rtl8723au/hal/rtl8723a_hal_init.c b/drivers/staging/rtl8723au/hal/rtl8723a_hal_init.c
new file mode 100644
index 000000000000..0982b0a4ab9b
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/rtl8723a_hal_init.c
@@ -0,0 +1,3452 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#define _HAL_INIT_C_
+
+#include <linux/firmware.h>
+#include <drv_types.h>
+#include <rtw_efuse.h>
+
+#include <rtl8723a_hal.h>
+
+static void _FWDownloadEnable(struct rtw_adapter *padapter, bool enable)
+{
+ u8 tmp;
+
+ if (enable) {
+ /* 8051 enable */
+ tmp = rtw_read8(padapter, REG_SYS_FUNC_EN + 1);
+ rtw_write8(padapter, REG_SYS_FUNC_EN + 1, tmp | 0x04);
+
+ /* MCU firmware download enable. */
+ tmp = rtw_read8(padapter, REG_MCUFWDL);
+ rtw_write8(padapter, REG_MCUFWDL, tmp | 0x01);
+
+ /* 8051 reset */
+ tmp = rtw_read8(padapter, REG_MCUFWDL + 2);
+ rtw_write8(padapter, REG_MCUFWDL + 2, tmp & 0xf7);
+ } else {
+ /* MCU firmware download disable. */
+ tmp = rtw_read8(padapter, REG_MCUFWDL);
+ rtw_write8(padapter, REG_MCUFWDL, tmp & 0xfe);
+
+ /* Reserved for fw extension. */
+ rtw_write8(padapter, REG_MCUFWDL + 1, 0x00);
+ }
+}
+
+static int _BlockWrite(struct rtw_adapter *padapter, void *buffer, u32 buffSize)
+{
+ int ret = _SUCCESS;
+ /* (Default) Phase #1 : PCI muse use 4-byte write to download FW */
+ u32 blockSize_p1 = 4;
+ /* Phase #2 : Use 8-byte, if Phase#1 use big size to write FW. */
+ u32 blockSize_p2 = 8;
+ /* Phase #3 : Use 1-byte, the remnant of FW image. */
+ u32 blockSize_p3 = 1;
+ u32 blockCount_p1 = 0, blockCount_p2 = 0, blockCount_p3 = 0;
+ u32 remainSize_p1 = 0, remainSize_p2 = 0;
+ u8 *bufferPtr = (u8 *) buffer;
+ u32 i = 0, offset = 0;
+
+ blockSize_p1 = 254;
+
+ /* 3 Phase #1 */
+ blockCount_p1 = buffSize / blockSize_p1;
+ remainSize_p1 = buffSize % blockSize_p1;
+
+ if (blockCount_p1) {
+ RT_TRACE(_module_hal_init_c_, _drv_notice_,
+ ("_BlockWrite: [P1] buffSize(%d) blockSize_p1(%d) "
+ "blockCount_p1(%d) remainSize_p1(%d)\n",
+ buffSize, blockSize_p1, blockCount_p1,
+ remainSize_p1));
+ }
+
+ for (i = 0; i < blockCount_p1; i++) {
+ ret = rtw_writeN(padapter,
+ (FW_8723A_START_ADDRESS + i * blockSize_p1),
+ blockSize_p1, (bufferPtr + i * blockSize_p1));
+ if (ret == _FAIL)
+ goto exit;
+ }
+
+ /* 3 Phase #2 */
+ if (remainSize_p1) {
+ offset = blockCount_p1 * blockSize_p1;
+
+ blockCount_p2 = remainSize_p1 / blockSize_p2;
+ remainSize_p2 = remainSize_p1 % blockSize_p2;
+
+ if (blockCount_p2) {
+ RT_TRACE(_module_hal_init_c_, _drv_notice_,
+ ("_BlockWrite: [P2] buffSize_p2(%d) "
+ "blockSize_p2(%d) blockCount_p2(%d) "
+ "remainSize_p2(%d)\n",
+ (buffSize - offset), blockSize_p2,
+ blockCount_p2, remainSize_p2));
+ }
+
+ for (i = 0; i < blockCount_p2; i++) {
+ ret = rtw_writeN(padapter,
+ (FW_8723A_START_ADDRESS + offset +
+ i * blockSize_p2), blockSize_p2,
+ (bufferPtr + offset +
+ i * blockSize_p2));
+
+ if (ret == _FAIL)
+ goto exit;
+ }
+ }
+
+ /* 3 Phase #3 */
+ if (remainSize_p2) {
+ offset = (blockCount_p1 * blockSize_p1) +
+ (blockCount_p2 * blockSize_p2);
+
+ blockCount_p3 = remainSize_p2 / blockSize_p3;
+
+ RT_TRACE(_module_hal_init_c_, _drv_notice_,
+ ("_BlockWrite: [P3] buffSize_p3(%d) blockSize_p3(%d) "
+ "blockCount_p3(%d)\n",
+ (buffSize - offset), blockSize_p3, blockCount_p3));
+
+ for (i = 0; i < blockCount_p3; i++) {
+ ret = rtw_write8(padapter,
+ (FW_8723A_START_ADDRESS + offset + i),
+ *(bufferPtr + offset + i));
+
+ if (ret == _FAIL)
+ goto exit;
+ }
+ }
+
+exit:
+ return ret;
+}
+
+static int
+_PageWrite(struct rtw_adapter *padapter, u32 page, void *buffer, u32 size)
+{
+ u8 value8;
+ u8 u8Page = (u8) (page & 0x07);
+
+ value8 = (rtw_read8(padapter, REG_MCUFWDL + 2) & 0xF8) | u8Page;
+ rtw_write8(padapter, REG_MCUFWDL + 2, value8);
+
+ return _BlockWrite(padapter, buffer, size);
+}
+
+static int _WriteFW(struct rtw_adapter *padapter, void *buffer, u32 size)
+{
+ /* Since we need dynamic decide method of dwonload fw, so we
+ call this function to get chip version. */
+ /* We can remove _ReadChipVersion from ReadpadapterInfo8192C later. */
+ int ret = _SUCCESS;
+ u32 pageNums, remainSize;
+ u32 page, offset;
+ u8 *bufferPtr = (u8 *) buffer;
+
+ pageNums = size / MAX_PAGE_SIZE;
+ /* RT_ASSERT((pageNums <= 4),
+ ("Page numbers should not greater then 4 \n")); */
+ remainSize = size % MAX_PAGE_SIZE;
+
+ for (page = 0; page < pageNums; page++) {
+ offset = page * MAX_PAGE_SIZE;
+ ret = _PageWrite(padapter, page, bufferPtr + offset,
+ MAX_PAGE_SIZE);
+
+ if (ret == _FAIL)
+ goto exit;
+ }
+ if (remainSize) {
+ offset = pageNums * MAX_PAGE_SIZE;
+ page = pageNums;
+ ret = _PageWrite(padapter, page, bufferPtr + offset,
+ remainSize);
+
+ if (ret == _FAIL)
+ goto exit;
+ }
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ ("_WriteFW Done- for Normal chip.\n"));
+
+exit:
+ return ret;
+}
+
+static s32 _FWFreeToGo(struct rtw_adapter *padapter)
+{
+ u32 counter = 0;
+ u32 value32;
+
+ /* polling CheckSum report */
+ do {
+ value32 = rtw_read32(padapter, REG_MCUFWDL);
+ if (value32 & FWDL_ChkSum_rpt)
+ break;
+ } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
+
+ if (counter >= POLLING_READY_TIMEOUT_COUNT) {
+ RT_TRACE(_module_hal_init_c_, _drv_err_,
+ ("%s: chksum report fail! REG_MCUFWDL:0x%08x\n",
+ __func__, value32));
+ return _FAIL;
+ }
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ ("%s: Checksum report OK! REG_MCUFWDL:0x%08x\n", __func__,
+ value32));
+
+ value32 = rtw_read32(padapter, REG_MCUFWDL);
+ value32 |= MCUFWDL_RDY;
+ value32 &= ~WINTINI_RDY;
+ rtw_write32(padapter, REG_MCUFWDL, value32);
+
+ /* polling for FW ready */
+ counter = 0;
+ do {
+ value32 = rtw_read32(padapter, REG_MCUFWDL);
+ if (value32 & WINTINI_RDY) {
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ ("%s: Polling FW ready success!! "
+ "REG_MCUFWDL:0x%08x\n",
+ __func__, value32));
+ return _SUCCESS;
+ }
+ udelay(5);
+ } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
+
+ RT_TRACE(_module_hal_init_c_, _drv_err_,
+ ("%s: Polling FW ready fail!! REG_MCUFWDL:0x%08x\n",
+ __func__, value32));
+ return _FAIL;
+}
+
+#define IS_FW_81xxC(padapter) (((GET_HAL_DATA(padapter))->FirmwareSignature & 0xFFF0) == 0x88C0)
+
+void rtl8723a_FirmwareSelfReset(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 u1bTmp;
+ u8 Delay = 100;
+
+ if (!(IS_FW_81xxC(padapter) &&
+ ((pHalData->FirmwareVersion < 0x21) ||
+ (pHalData->FirmwareVersion == 0x21 &&
+ pHalData->FirmwareSubVersion < 0x01)))) {
+ /* after 88C Fw v33.1 */
+ /* 0x1cf = 0x20. Inform 8051 to reset. 2009.12.25. tynli_test */
+ rtw_write8(padapter, REG_HMETFR + 3, 0x20);
+
+ u1bTmp = rtw_read8(padapter, REG_SYS_FUNC_EN + 1);
+ while (u1bTmp & BIT2) {
+ Delay--;
+ if (Delay == 0)
+ break;
+ udelay(50);
+ u1bTmp = rtw_read8(padapter, REG_SYS_FUNC_EN + 1);
+ }
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ ("-%s: 8051 reset success (%d)\n", __func__,
+ Delay));
+
+ if ((Delay == 0)) {
+ /* force firmware reset */
+ u1bTmp = rtw_read8(padapter, REG_SYS_FUNC_EN + 1);
+ rtw_write8(padapter, REG_SYS_FUNC_EN + 1,
+ u1bTmp & (~BIT2));
+ }
+ }
+}
+
+/* */
+/* Description: */
+/* Download 8192C firmware code. */
+/* */
+/* */
+s32 rtl8723a_FirmwareDownload(struct rtw_adapter *padapter)
+{
+ s32 rtStatus = _SUCCESS;
+ u8 writeFW_retry = 0;
+ unsigned long fwdl_start_time;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct dvobj_priv *dvobj = adapter_to_dvobj(padapter);
+ struct device *device = dvobj_to_dev(dvobj);
+ struct rt_8723a_firmware_hdr *pFwHdr = NULL;
+ const struct firmware *fw;
+ char *fw_name;
+ u8 *firmware_buf = NULL;
+ u8 *buf;
+ int fw_size;
+ static int log_version;
+
+ RT_TRACE(_module_hal_init_c_, _drv_info_, ("+%s\n", __func__));
+
+ if (IS_8723A_A_CUT(pHalData->VersionID)) {
+ fw_name = "rtlwifi/rtl8723aufw.bin";
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ ("rtl8723a_FirmwareDownload: R8723FwImageArray_UMC "
+ "for RTL8723A A CUT\n"));
+ } else if (IS_8723A_B_CUT(pHalData->VersionID)) {
+ /* WLAN Fw. */
+ if (padapter->registrypriv.wifi_spec == 1) {
+ fw_name = "rtlwifi/rtl8723aufw_B_NoBT.bin";
+ DBG_8723A(" Rtl8723_FwUMCBCutImageArrayWithoutBT for "
+ "RTL8723A B CUT\n");
+ } else {
+#ifdef CONFIG_8723AU_BT_COEXIST
+ fw_name = "rtlwifi/rtl8723aufw_B.bin";
+ DBG_8723A(" Rtl8723_FwUMCBCutImageArrayWithBT for "
+ "RTL8723A B CUT\n");
+#else
+ fw_name = "rtlwifi/rtl8723aufw_B_NoBT.bin";
+ DBG_8723A(" Rtl8723_FwUMCBCutImageArrayWithoutBT for "
+ "RTL8723A B CUT\n");
+#endif
+ }
+ } else {
+ /* <Roger_TODO> We should download proper RAM Code here
+ to match the ROM code. */
+ RT_TRACE(_module_hal_init_c_, _drv_err_,
+ ("%s: unknow version!\n", __func__));
+ rtStatus = _FAIL;
+ goto Exit;
+ }
+
+ pr_info("rtl8723au: Loading firmware %s\n", fw_name);
+ if (request_firmware(&fw, fw_name, device)) {
+ pr_err("rtl8723au: request_firmware load failed\n");
+ rtStatus = _FAIL;
+ goto Exit;
+ }
+ if (!fw) {
+ pr_err("rtl8723au: Firmware %s not available\n", fw_name);
+ rtStatus = _FAIL;
+ goto Exit;
+ }
+ firmware_buf = kzalloc(fw->size, GFP_KERNEL);
+ if (!firmware_buf) {
+ rtStatus = _FAIL;
+ goto Exit;
+ }
+ memcpy(firmware_buf, fw->data, fw->size);
+ buf = firmware_buf;
+ fw_size = fw->size;
+ release_firmware(fw);
+
+ /* To Check Fw header. Added by tynli. 2009.12.04. */
+ pFwHdr = (struct rt_8723a_firmware_hdr *)firmware_buf;
+
+ pHalData->FirmwareVersion = le16_to_cpu(pFwHdr->Version);
+ pHalData->FirmwareSubVersion = pFwHdr->Subversion;
+ pHalData->FirmwareSignature = le16_to_cpu(pFwHdr->Signature);
+
+ DBG_8723A("%s: fw_ver =%d fw_subver =%d sig = 0x%x\n",
+ __func__, pHalData->FirmwareVersion,
+ pHalData->FirmwareSubVersion, pHalData->FirmwareSignature);
+
+ if (!log_version++)
+ pr_info("%sFirmware Version %d, SubVersion %d, Signature "
+ "0x%x\n", DRIVER_PREFIX, pHalData->FirmwareVersion,
+ pHalData->FirmwareSubVersion,
+ pHalData->FirmwareSignature);
+
+ if (IS_FW_HEADER_EXIST(pFwHdr)) {
+ /* Shift 32 bytes for FW header */
+ buf = buf + 32;
+ fw_size = fw_size - 32;
+ }
+
+ /* Suggested by Filen. If 8051 is running in RAM code, driver should
+ inform Fw to reset by itself, */
+ /* or it will cause download Fw fail. 2010.02.01. by tynli. */
+ if (rtw_read8(padapter, REG_MCUFWDL) & RAM_DL_SEL) {
+ /* 8051 RAM code */
+ rtl8723a_FirmwareSelfReset(padapter);
+ rtw_write8(padapter, REG_MCUFWDL, 0x00);
+ }
+
+ _FWDownloadEnable(padapter, true);
+ fwdl_start_time = jiffies;
+ while (1) {
+ /* reset the FWDL chksum */
+ rtw_write8(padapter, REG_MCUFWDL,
+ rtw_read8(padapter, REG_MCUFWDL) | FWDL_ChkSum_rpt);
+
+ rtStatus = _WriteFW(padapter, buf, fw_size);
+
+ if (rtStatus == _SUCCESS ||
+ (jiffies_to_msecs(jiffies - fwdl_start_time) > 500 &&
+ writeFW_retry++ >= 3))
+ break;
+
+ DBG_8723A("%s writeFW_retry:%u, time after fwdl_start_time:"
+ "%ums\n", __func__, writeFW_retry,
+ jiffies_to_msecs(jiffies - fwdl_start_time));
+ }
+ _FWDownloadEnable(padapter, false);
+ if (_SUCCESS != rtStatus) {
+ DBG_8723A("DL Firmware failed!\n");
+ goto Exit;
+ }
+
+ rtStatus = _FWFreeToGo(padapter);
+ if (_SUCCESS != rtStatus) {
+ RT_TRACE(_module_hal_init_c_, _drv_err_,
+ ("DL Firmware failed!\n"));
+ goto Exit;
+ }
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ ("Firmware is ready to run!\n"));
+
+Exit:
+ kfree(firmware_buf);
+ return rtStatus;
+}
+
+void rtl8723a_InitializeFirmwareVars(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ /* Init Fw LPS related. */
+ padapter->pwrctrlpriv.bFwCurrentInPSMode = false;
+
+ /* Init H2C counter. by tynli. 2009.12.09. */
+ pHalData->LastHMEBoxNum = 0;
+}
+
+static void rtl8723a_free_hal_data(struct rtw_adapter *padapter)
+{
+
+ kfree(padapter->HalData);
+ padapter->HalData = NULL;
+
+}
+
+/* */
+/* Efuse related code */
+/* */
+static u8
+hal_EfuseSwitchToBank(struct rtw_adapter *padapter, u8 bank)
+{
+ u8 bRet = false;
+ u32 value32 = 0;
+
+ DBG_8723A("%s: Efuse switch bank to %d\n", __func__, bank);
+ value32 = rtw_read32(padapter, EFUSE_TEST);
+ bRet = true;
+ switch (bank) {
+ case 0:
+ value32 = (value32 & ~EFUSE_SEL_MASK) |
+ EFUSE_SEL(EFUSE_WIFI_SEL_0);
+ break;
+ case 1:
+ value32 = (value32 & ~EFUSE_SEL_MASK) |
+ EFUSE_SEL(EFUSE_BT_SEL_0);
+ break;
+ case 2:
+ value32 = (value32 & ~EFUSE_SEL_MASK) |
+ EFUSE_SEL(EFUSE_BT_SEL_1);
+ break;
+ case 3:
+ value32 = (value32 & ~EFUSE_SEL_MASK) |
+ EFUSE_SEL(EFUSE_BT_SEL_2);
+ break;
+ default:
+ value32 = (value32 & ~EFUSE_SEL_MASK) |
+ EFUSE_SEL(EFUSE_WIFI_SEL_0);
+ bRet = false;
+ break;
+ }
+ rtw_write32(padapter, EFUSE_TEST, value32);
+
+ return bRet;
+}
+
+static void
+Hal_GetEfuseDefinition(struct rtw_adapter *padapter,
+ u8 efuseType, u8 type, void *pOut)
+{
+ u8 *pu1Tmp;
+ u16 *pu2Tmp;
+ u8 *pMax_section;
+
+ switch (type) {
+ case TYPE_EFUSE_MAX_SECTION:
+ pMax_section = (u8 *) pOut;
+
+ if (efuseType == EFUSE_WIFI)
+ *pMax_section = EFUSE_MAX_SECTION_8723A;
+ else
+ *pMax_section = EFUSE_BT_MAX_SECTION;
+ break;
+
+ case TYPE_EFUSE_REAL_CONTENT_LEN:
+ pu2Tmp = (u16 *) pOut;
+
+ if (efuseType == EFUSE_WIFI)
+ *pu2Tmp = EFUSE_REAL_CONTENT_LEN_8723A;
+ else
+ *pu2Tmp = EFUSE_BT_REAL_CONTENT_LEN;
+ break;
+
+ case TYPE_AVAILABLE_EFUSE_BYTES_BANK:
+ pu2Tmp = (u16 *) pOut;
+
+ if (efuseType == EFUSE_WIFI)
+ *pu2Tmp = (EFUSE_REAL_CONTENT_LEN_8723A -
+ EFUSE_OOB_PROTECT_BYTES);
+ else
+ *pu2Tmp = (EFUSE_BT_REAL_BANK_CONTENT_LEN -
+ EFUSE_PROTECT_BYTES_BANK);
+ break;
+
+ case TYPE_AVAILABLE_EFUSE_BYTES_TOTAL:
+ pu2Tmp = (u16 *) pOut;
+
+ if (efuseType == EFUSE_WIFI)
+ *pu2Tmp = (EFUSE_REAL_CONTENT_LEN_8723A -
+ EFUSE_OOB_PROTECT_BYTES);
+ else
+ *pu2Tmp = (EFUSE_BT_REAL_CONTENT_LEN -
+ (EFUSE_PROTECT_BYTES_BANK * 3));
+ break;
+
+ case TYPE_EFUSE_MAP_LEN:
+ pu2Tmp = (u16 *) pOut;
+
+ if (efuseType == EFUSE_WIFI)
+ *pu2Tmp = EFUSE_MAP_LEN_8723A;
+ else
+ *pu2Tmp = EFUSE_BT_MAP_LEN;
+ break;
+
+ case TYPE_EFUSE_PROTECT_BYTES_BANK:
+ pu1Tmp = (u8 *) pOut;
+
+ if (efuseType == EFUSE_WIFI)
+ *pu1Tmp = EFUSE_OOB_PROTECT_BYTES;
+ else
+ *pu1Tmp = EFUSE_PROTECT_BYTES_BANK;
+ break;
+
+ case TYPE_EFUSE_CONTENT_LEN_BANK:
+ pu2Tmp = (u16 *) pOut;
+
+ if (efuseType == EFUSE_WIFI)
+ *pu2Tmp = EFUSE_REAL_CONTENT_LEN_8723A;
+ else
+ *pu2Tmp = EFUSE_BT_REAL_BANK_CONTENT_LEN;
+ break;
+
+ default:
+ pu1Tmp = (u8 *) pOut;
+ *pu1Tmp = 0;
+ break;
+ }
+}
+
+#define VOLTAGE_V25 0x03
+#define LDOE25_SHIFT 28
+
+static void
+Hal_EfusePowerSwitch(struct rtw_adapter *padapter, u8 bWrite, u8 PwrState)
+{
+ u8 tempval;
+ u16 tmpV16;
+
+ if (PwrState == true) {
+ rtw_write8(padapter, REG_EFUSE_ACCESS, EFUSE_ACCESS_ON);
+
+ /* 1.2V Power: From VDDON with Power
+ Cut(0x0000h[15]), defualt valid */
+ tmpV16 = rtw_read16(padapter, REG_SYS_ISO_CTRL);
+ if (!(tmpV16 & PWC_EV12V)) {
+ tmpV16 |= PWC_EV12V;
+ rtw_write16(padapter, REG_SYS_ISO_CTRL, tmpV16);
+ }
+ /* Reset: 0x0000h[28], default valid */
+ tmpV16 = rtw_read16(padapter, REG_SYS_FUNC_EN);
+ if (!(tmpV16 & FEN_ELDR)) {
+ tmpV16 |= FEN_ELDR;
+ rtw_write16(padapter, REG_SYS_FUNC_EN, tmpV16);
+ }
+
+ /* Clock: Gated(0x0008h[5]) 8M(0x0008h[1]) clock
+ from ANA, default valid */
+ tmpV16 = rtw_read16(padapter, REG_SYS_CLKR);
+ if ((!(tmpV16 & LOADER_CLK_EN)) || (!(tmpV16 & ANA8M))) {
+ tmpV16 |= (LOADER_CLK_EN | ANA8M);
+ rtw_write16(padapter, REG_SYS_CLKR, tmpV16);
+ }
+
+ if (bWrite == true) {
+ /* Enable LDO 2.5V before read/write action */
+ tempval = rtw_read8(padapter, EFUSE_TEST + 3);
+ tempval &= 0x0F;
+ tempval |= (VOLTAGE_V25 << 4);
+ rtw_write8(padapter, EFUSE_TEST + 3, (tempval | 0x80));
+ }
+ } else {
+ rtw_write8(padapter, REG_EFUSE_ACCESS, EFUSE_ACCESS_OFF);
+
+ if (bWrite == true) {
+ /* Disable LDO 2.5V after read/write action */
+ tempval = rtw_read8(padapter, EFUSE_TEST + 3);
+ rtw_write8(padapter, EFUSE_TEST + 3, (tempval & 0x7F));
+ }
+ }
+}
+
+static void
+hal_ReadEFuse_WiFi(struct rtw_adapter *padapter,
+ u16 _offset, u16 _size_byte, u8 *pbuf)
+{
+ u8 *efuseTbl = NULL;
+ u16 eFuse_Addr = 0;
+ u8 offset, wden;
+ u8 efuseHeader, efuseExtHdr, efuseData;
+ u16 i, total, used;
+
+ /* Do NOT excess total size of EFuse table.
+ Added by Roger, 2008.11.10. */
+ if ((_offset + _size_byte) > EFUSE_MAP_LEN_8723A) {
+ DBG_8723A("%s: Invalid offset(%#x) with read bytes(%#x)!!\n",
+ __func__, _offset, _size_byte);
+ return;
+ }
+
+ efuseTbl = (u8 *) kmalloc(EFUSE_MAP_LEN_8723A, GFP_KERNEL);
+ if (efuseTbl == NULL) {
+ DBG_8723A("%s: alloc efuseTbl fail!\n", __func__);
+ return;
+ }
+ /* 0xff will be efuse default value instead of 0x00. */
+ memset(efuseTbl, 0xFF, EFUSE_MAP_LEN_8723A);
+
+ /* switch bank back to bank 0 for later BT and wifi use. */
+ hal_EfuseSwitchToBank(padapter, 0);
+
+ while (AVAILABLE_EFUSE_ADDR(eFuse_Addr)) {
+ ReadEFuseByte23a(padapter, eFuse_Addr++, &efuseHeader);
+ if (efuseHeader == 0xFF) {
+ DBG_8723A("%s: data end at address =%#x\n", __func__,
+ eFuse_Addr);
+ break;
+ }
+
+ /* Check PG header for section num. */
+ if (EXT_HEADER(efuseHeader)) { /* extended header */
+ offset = GET_HDR_OFFSET_2_0(efuseHeader);
+
+ ReadEFuseByte23a(padapter, eFuse_Addr++, &efuseExtHdr);
+ if (ALL_WORDS_DISABLED(efuseExtHdr)) {
+ continue;
+ }
+
+ offset |= ((efuseExtHdr & 0xF0) >> 1);
+ wden = (efuseExtHdr & 0x0F);
+ } else {
+ offset = ((efuseHeader >> 4) & 0x0f);
+ wden = (efuseHeader & 0x0f);
+ }
+
+ if (offset < EFUSE_MAX_SECTION_8723A) {
+ u16 addr;
+ /* Get word enable value from PG header */
+
+ addr = offset * PGPKT_DATA_SIZE;
+ for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) {
+ /* Check word enable condition in the section */
+ if (!(wden & (0x01 << i))) {
+ ReadEFuseByte23a(padapter, eFuse_Addr++,
+ &efuseData);
+ efuseTbl[addr] = efuseData;
+
+ ReadEFuseByte23a(padapter, eFuse_Addr++,
+ &efuseData);
+ efuseTbl[addr + 1] = efuseData;
+ }
+ addr += 2;
+ }
+ } else {
+ DBG_8723A(KERN_ERR "%s: offset(%d) is illegal!!\n",
+ __func__, offset);
+ eFuse_Addr += Efuse_CalculateWordCnts23a(wden) * 2;
+ }
+ }
+
+ /* Copy from Efuse map to output pointer memory!!! */
+ for (i = 0; i < _size_byte; i++)
+ pbuf[i] = efuseTbl[_offset + i];
+
+ /* Calculate Efuse utilization */
+ EFUSE_GetEfuseDefinition23a(padapter, EFUSE_WIFI,
+ TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, &total);
+ used = eFuse_Addr - 1;
+ rtw_hal_set_hwreg23a(padapter, HW_VAR_EFUSE_BYTES, (u8 *)&used);
+
+ kfree(efuseTbl);
+}
+
+static void
+hal_ReadEFuse_BT(struct rtw_adapter *padapter,
+ u16 _offset, u16 _size_byte, u8 *pbuf)
+{
+ u8 *efuseTbl;
+ u8 bank;
+ u16 eFuse_Addr;
+ u8 efuseHeader, efuseExtHdr, efuseData;
+ u8 offset, wden;
+ u16 i, total, used;
+
+ /* Do NOT excess total size of EFuse table.
+ Added by Roger, 2008.11.10. */
+ if ((_offset + _size_byte) > EFUSE_BT_MAP_LEN) {
+ DBG_8723A("%s: Invalid offset(%#x) with read bytes(%#x)!!\n",
+ __func__, _offset, _size_byte);
+ return;
+ }
+
+ efuseTbl = kmalloc(EFUSE_BT_MAP_LEN, GFP_KERNEL);
+ if (efuseTbl == NULL) {
+ DBG_8723A("%s: efuseTbl malloc fail!\n", __func__);
+ return;
+ }
+ /* 0xff will be efuse default value instead of 0x00. */
+ memset(efuseTbl, 0xFF, EFUSE_BT_MAP_LEN);
+
+ EFUSE_GetEfuseDefinition23a(padapter, EFUSE_BT,
+ TYPE_AVAILABLE_EFUSE_BYTES_BANK, &total);
+
+ for (bank = 1; bank < EFUSE_MAX_BANK; bank++) {
+ if (hal_EfuseSwitchToBank(padapter, bank) == false) {
+ DBG_8723A("%s: hal_EfuseSwitchToBank Fail!!\n",
+ __func__);
+ goto exit;
+ }
+
+ eFuse_Addr = 0;
+
+ while (AVAILABLE_EFUSE_ADDR(eFuse_Addr)) {
+ ReadEFuseByte23a(padapter, eFuse_Addr++, &efuseHeader);
+ if (efuseHeader == 0xFF)
+ break;
+
+ /* Check PG header for section num. */
+ if (EXT_HEADER(efuseHeader)) { /* extended header */
+ offset = GET_HDR_OFFSET_2_0(efuseHeader);
+
+ ReadEFuseByte23a(padapter, eFuse_Addr++,
+ &efuseExtHdr);
+ if (ALL_WORDS_DISABLED(efuseExtHdr)) {
+ continue;
+ }
+
+ offset |= ((efuseExtHdr & 0xF0) >> 1);
+ wden = (efuseExtHdr & 0x0F);
+ } else {
+ offset = ((efuseHeader >> 4) & 0x0f);
+ wden = (efuseHeader & 0x0f);
+ }
+
+ if (offset < EFUSE_BT_MAX_SECTION) {
+ u16 addr;
+
+ /* Get word enable value from PG header */
+
+ addr = offset * PGPKT_DATA_SIZE;
+ for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) {
+ /* Check word enable condition in
+ the section */
+ if (!(wden & (0x01 << i))) {
+ ReadEFuseByte23a(padapter,
+ eFuse_Addr++,
+ &efuseData);
+ efuseTbl[addr] = efuseData;
+
+ ReadEFuseByte23a(padapter,
+ eFuse_Addr++,
+ &efuseData);
+ efuseTbl[addr + 1] = efuseData;
+ }
+ addr += 2;
+ }
+ } else {
+ DBG_8723A(KERN_ERR
+ "%s: offset(%d) is illegal!!\n",
+ __func__, offset);
+ eFuse_Addr += Efuse_CalculateWordCnts23a(wden) * 2;
+ }
+ }
+
+ if ((eFuse_Addr - 1) < total) {
+ DBG_8723A("%s: bank(%d) data end at %#x\n",
+ __func__, bank, eFuse_Addr - 1);
+ break;
+ }
+ }
+
+ /* switch bank back to bank 0 for later BT and wifi use. */
+ hal_EfuseSwitchToBank(padapter, 0);
+
+ /* Copy from Efuse map to output pointer memory!!! */
+ for (i = 0; i < _size_byte; i++)
+ pbuf[i] = efuseTbl[_offset + i];
+
+ /* */
+ /* Calculate Efuse utilization. */
+ /* */
+ EFUSE_GetEfuseDefinition23a(padapter, EFUSE_BT,
+ TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, &total);
+ used = (EFUSE_BT_REAL_BANK_CONTENT_LEN * (bank - 1)) + eFuse_Addr - 1;
+ rtw_hal_set_hwreg23a(padapter, HW_VAR_EFUSE_BT_BYTES, (u8 *) &used);
+
+exit:
+ kfree(efuseTbl);
+}
+
+static void
+Hal_ReadEFuse(struct rtw_adapter *padapter,
+ u8 efuseType, u16 _offset, u16 _size_byte, u8 *pbuf)
+{
+ if (efuseType == EFUSE_WIFI)
+ hal_ReadEFuse_WiFi(padapter, _offset, _size_byte, pbuf);
+ else
+ hal_ReadEFuse_BT(padapter, _offset, _size_byte, pbuf);
+}
+
+static u16
+hal_EfuseGetCurrentSize_WiFi(struct rtw_adapter *padapter)
+{
+ u16 efuse_addr = 0;
+ u8 hoffset = 0, hworden = 0;
+ u8 efuse_data, word_cnts = 0;
+
+ rtw23a_hal_get_hwreg(padapter, HW_VAR_EFUSE_BYTES, (u8 *) &efuse_addr);
+
+ DBG_8723A("%s: start_efuse_addr = 0x%X\n", __func__, efuse_addr);
+
+ /* switch bank back to bank 0 for later BT and wifi use. */
+ hal_EfuseSwitchToBank(padapter, 0);
+
+ while (AVAILABLE_EFUSE_ADDR(efuse_addr)) {
+ if (efuse_OneByteRead23a(padapter, efuse_addr, &efuse_data) ==
+ false) {
+ DBG_8723A(KERN_ERR "%s: efuse_OneByteRead23a Fail! "
+ "addr = 0x%X !!\n", __func__, efuse_addr);
+ break;
+ }
+
+ if (efuse_data == 0xFF)
+ break;
+
+ if (EXT_HEADER(efuse_data)) {
+ hoffset = GET_HDR_OFFSET_2_0(efuse_data);
+ efuse_addr++;
+ efuse_OneByteRead23a(padapter, efuse_addr, &efuse_data);
+ if (ALL_WORDS_DISABLED(efuse_data)) {
+ continue;
+ }
+
+ hoffset |= ((efuse_data & 0xF0) >> 1);
+ hworden = efuse_data & 0x0F;
+ } else {
+ hoffset = (efuse_data >> 4) & 0x0F;
+ hworden = efuse_data & 0x0F;
+ }
+
+ word_cnts = Efuse_CalculateWordCnts23a(hworden);
+ efuse_addr += (word_cnts * 2) + 1;
+ }
+
+ rtw_hal_set_hwreg23a(padapter, HW_VAR_EFUSE_BYTES, (u8 *) &efuse_addr);
+
+ DBG_8723A("%s: CurrentSize =%d\n", __func__, efuse_addr);
+
+ return efuse_addr;
+}
+
+static u16
+hal_EfuseGetCurrentSize_BT(struct rtw_adapter *padapter)
+{
+ u16 btusedbytes;
+ u16 efuse_addr;
+ u8 bank, startBank;
+ u8 hoffset = 0, hworden = 0;
+ u8 efuse_data, word_cnts = 0;
+ u16 retU2 = 0;
+
+ rtw23a_hal_get_hwreg(padapter, HW_VAR_EFUSE_BT_BYTES, (u8 *) &btusedbytes);
+
+ efuse_addr = (u16) ((btusedbytes % EFUSE_BT_REAL_BANK_CONTENT_LEN));
+ startBank = (u8) (1 + (btusedbytes / EFUSE_BT_REAL_BANK_CONTENT_LEN));
+
+ DBG_8723A("%s: start from bank =%d addr = 0x%X\n", __func__, startBank,
+ efuse_addr);
+
+ EFUSE_GetEfuseDefinition23a(padapter, EFUSE_BT,
+ TYPE_AVAILABLE_EFUSE_BYTES_BANK, &retU2);
+
+ for (bank = startBank; bank < EFUSE_MAX_BANK; bank++) {
+ if (hal_EfuseSwitchToBank(padapter, bank) == false) {
+ DBG_8723A(KERN_ERR "%s: switch bank(%d) Fail!!\n",
+ __func__, bank);
+ bank = EFUSE_MAX_BANK;
+ break;
+ }
+
+ /* only when bank is switched we have to reset
+ the efuse_addr. */
+ if (bank != startBank)
+ efuse_addr = 0;
+
+ while (AVAILABLE_EFUSE_ADDR(efuse_addr)) {
+ if (efuse_OneByteRead23a(padapter, efuse_addr,
+ &efuse_data) == false) {
+ DBG_8723A(KERN_ERR "%s: efuse_OneByteRead23a Fail!"
+ " addr = 0x%X !!\n",
+ __func__, efuse_addr);
+ bank = EFUSE_MAX_BANK;
+ break;
+ }
+
+ if (efuse_data == 0xFF)
+ break;
+
+ if (EXT_HEADER(efuse_data)) {
+ hoffset = GET_HDR_OFFSET_2_0(efuse_data);
+ efuse_addr++;
+ efuse_OneByteRead23a(padapter, efuse_addr,
+ &efuse_data);
+ if (ALL_WORDS_DISABLED(efuse_data)) {
+ efuse_addr++;
+ continue;
+ }
+
+ hoffset |= ((efuse_data & 0xF0) >> 1);
+ hworden = efuse_data & 0x0F;
+ } else {
+ hoffset = (efuse_data >> 4) & 0x0F;
+ hworden = efuse_data & 0x0F;
+ }
+ word_cnts = Efuse_CalculateWordCnts23a(hworden);
+ /* read next header */
+ efuse_addr += (word_cnts * 2) + 1;
+ }
+
+ /* Check if we need to check next bank efuse */
+ if (efuse_addr < retU2) {
+ break; /* don't need to check next bank. */
+ }
+ }
+
+ retU2 = ((bank - 1) * EFUSE_BT_REAL_BANK_CONTENT_LEN) + efuse_addr;
+ rtw_hal_set_hwreg23a(padapter, HW_VAR_EFUSE_BT_BYTES, (u8 *)&retU2);
+
+ DBG_8723A("%s: CurrentSize =%d\n", __func__, retU2);
+ return retU2;
+}
+
+static u16
+Hal_EfuseGetCurrentSize(struct rtw_adapter *pAdapter, u8 efuseType)
+{
+ u16 ret = 0;
+
+ if (efuseType == EFUSE_WIFI)
+ ret = hal_EfuseGetCurrentSize_WiFi(pAdapter);
+ else
+ ret = hal_EfuseGetCurrentSize_BT(pAdapter);
+
+ return ret;
+}
+
+static u8
+Hal_EfuseWordEnableDataWrite(struct rtw_adapter *padapter,
+ u16 efuse_addr, u8 word_en, u8 *data)
+{
+ u16 tmpaddr = 0;
+ u16 start_addr = efuse_addr;
+ u8 badworden = 0x0F;
+ u8 tmpdata[PGPKT_DATA_SIZE];
+
+ memset(tmpdata, 0xFF, PGPKT_DATA_SIZE);
+
+ if (!(word_en & BIT(0))) {
+ tmpaddr = start_addr;
+ efuse_OneByteWrite23a(padapter, start_addr++, data[0]);
+ efuse_OneByteWrite23a(padapter, start_addr++, data[1]);
+
+ efuse_OneByteRead23a(padapter, tmpaddr, &tmpdata[0]);
+ efuse_OneByteRead23a(padapter, tmpaddr + 1, &tmpdata[1]);
+ if ((data[0] != tmpdata[0]) || (data[1] != tmpdata[1])) {
+ badworden &= (~BIT(0));
+ }
+ }
+ if (!(word_en & BIT(1))) {
+ tmpaddr = start_addr;
+ efuse_OneByteWrite23a(padapter, start_addr++, data[2]);
+ efuse_OneByteWrite23a(padapter, start_addr++, data[3]);
+
+ efuse_OneByteRead23a(padapter, tmpaddr, &tmpdata[2]);
+ efuse_OneByteRead23a(padapter, tmpaddr + 1, &tmpdata[3]);
+ if ((data[2] != tmpdata[2]) || (data[3] != tmpdata[3])) {
+ badworden &= (~BIT(1));
+ }
+ }
+ if (!(word_en & BIT(2))) {
+ tmpaddr = start_addr;
+ efuse_OneByteWrite23a(padapter, start_addr++, data[4]);
+ efuse_OneByteWrite23a(padapter, start_addr++, data[5]);
+
+ efuse_OneByteRead23a(padapter, tmpaddr, &tmpdata[4]);
+ efuse_OneByteRead23a(padapter, tmpaddr + 1, &tmpdata[5]);
+ if ((data[4] != tmpdata[4]) || (data[5] != tmpdata[5])) {
+ badworden &= (~BIT(2));
+ }
+ }
+ if (!(word_en & BIT(3))) {
+ tmpaddr = start_addr;
+ efuse_OneByteWrite23a(padapter, start_addr++, data[6]);
+ efuse_OneByteWrite23a(padapter, start_addr++, data[7]);
+
+ efuse_OneByteRead23a(padapter, tmpaddr, &tmpdata[6]);
+ efuse_OneByteRead23a(padapter, tmpaddr + 1, &tmpdata[7]);
+ if ((data[6] != tmpdata[6]) || (data[7] != tmpdata[7])) {
+ badworden &= (~BIT(3));
+ }
+ }
+
+ return badworden;
+}
+
+static s32
+Hal_EfusePgPacketRead(struct rtw_adapter *padapter, u8 offset, u8 *data)
+{
+ u8 efuse_data, word_cnts = 0;
+ u16 efuse_addr = 0;
+ u8 hoffset = 0, hworden = 0;
+ u8 i;
+ u8 max_section = 0;
+ s32 ret;
+
+ if (data == NULL)
+ return false;
+
+ EFUSE_GetEfuseDefinition23a(padapter, EFUSE_WIFI, TYPE_EFUSE_MAX_SECTION,
+ &max_section);
+ if (offset > max_section) {
+ DBG_8723A("%s: Packet offset(%d) is illegal(>%d)!\n",
+ __func__, offset, max_section);
+ return false;
+ }
+
+ memset(data, 0xFF, PGPKT_DATA_SIZE);
+ ret = true;
+
+ /* */
+ /* <Roger_TODO> Efuse has been pre-programmed dummy 5Bytes at the
+ end of Efuse by CP. */
+ /* Skip dummy parts to prevent unexpected data read from Efuse. */
+ /* By pass right now. 2009.02.19. */
+ /* */
+ while (AVAILABLE_EFUSE_ADDR(efuse_addr)) {
+ if (efuse_OneByteRead23a(padapter, efuse_addr++, &efuse_data) ==
+ false) {
+ ret = false;
+ break;
+ }
+
+ if (efuse_data == 0xFF)
+ break;
+
+ if (EXT_HEADER(efuse_data)) {
+ hoffset = GET_HDR_OFFSET_2_0(efuse_data);
+ efuse_OneByteRead23a(padapter, efuse_addr++, &efuse_data);
+ if (ALL_WORDS_DISABLED(efuse_data)) {
+ DBG_8723A("%s: Error!! All words disabled!\n",
+ __func__);
+ continue;
+ }
+
+ hoffset |= ((efuse_data & 0xF0) >> 1);
+ hworden = efuse_data & 0x0F;
+ } else {
+ hoffset = (efuse_data >> 4) & 0x0F;
+ hworden = efuse_data & 0x0F;
+ }
+
+ if (hoffset == offset) {
+ for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) {
+ /* Check word enable condition in the section */
+ if (!(hworden & (0x01 << i))) {
+ ReadEFuseByte23a(padapter, efuse_addr++,
+ &efuse_data);
+ data[i * 2] = efuse_data;
+
+ ReadEFuseByte23a(padapter, efuse_addr++,
+ &efuse_data);
+ data[(i * 2) + 1] = efuse_data;
+ }
+ }
+ } else {
+ word_cnts = Efuse_CalculateWordCnts23a(hworden);
+ efuse_addr += word_cnts * 2;
+ }
+ }
+
+ return ret;
+}
+
+static u8
+hal_EfusePgCheckAvailableAddr(struct rtw_adapter *pAdapter, u8 efuseType)
+{
+ u16 max_available = 0;
+ u16 current_size;
+
+ EFUSE_GetEfuseDefinition23a(pAdapter, efuseType,
+ TYPE_AVAILABLE_EFUSE_BYTES_TOTAL,
+ &max_available);
+
+ current_size = Efuse_GetCurrentSize23a(pAdapter, efuseType);
+ if (current_size >= max_available) {
+ DBG_8723A("%s: Error!! current_size(%d)>max_available(%d)\n",
+ __func__, current_size, max_available);
+ return false;
+ }
+ return true;
+}
+
+static void
+hal_EfuseConstructPGPkt(u8 offset, u8 word_en, u8 *pData,
+ struct pg_pkt_struct *pTargetPkt)
+{
+ memset(pTargetPkt->data, 0xFF, PGPKT_DATA_SIZE);
+ pTargetPkt->offset = offset;
+ pTargetPkt->word_en = word_en;
+ efuse_WordEnableDataRead23a(word_en, pData, pTargetPkt->data);
+ pTargetPkt->word_cnts = Efuse_CalculateWordCnts23a(pTargetPkt->word_en);
+}
+
+static u8
+hal_EfusePartialWriteCheck(struct rtw_adapter *padapter, u8 efuseType,
+ u16 *pAddr, struct pg_pkt_struct *pTargetPkt)
+{
+ u8 bRet = false;
+ u16 startAddr = 0, efuse_max_available_len = 0, efuse_max = 0;
+ u8 efuse_data = 0;
+
+ EFUSE_GetEfuseDefinition23a(padapter, efuseType,
+ TYPE_AVAILABLE_EFUSE_BYTES_TOTAL,
+ &efuse_max_available_len);
+ EFUSE_GetEfuseDefinition23a(padapter, efuseType,
+ TYPE_EFUSE_CONTENT_LEN_BANK, &efuse_max);
+
+ if (efuseType == EFUSE_WIFI) {
+ rtw23a_hal_get_hwreg(padapter, HW_VAR_EFUSE_BYTES,
+ (u8 *) &startAddr);
+ } else {
+ rtw23a_hal_get_hwreg(padapter, HW_VAR_EFUSE_BT_BYTES,
+ (u8 *) &startAddr);
+ }
+ startAddr %= efuse_max;
+
+ while (1) {
+ if (startAddr >= efuse_max_available_len) {
+ bRet = false;
+ DBG_8723A("%s: startAddr(%d) >= efuse_max_available_"
+ "len(%d)\n", __func__, startAddr,
+ efuse_max_available_len);
+ break;
+ }
+
+ if (efuse_OneByteRead23a(padapter, startAddr, &efuse_data) &&
+ (efuse_data != 0xFF)) {
+ bRet = false;
+ DBG_8723A("%s: Something Wrong! last bytes(%#X = 0x%02X) "
+ "is not 0xFF\n", __func__,
+ startAddr, efuse_data);
+ break;
+ } else {
+ /* not used header, 0xff */
+ *pAddr = startAddr