// SPDX-License-Identifier: GPL-2.0
/******************************************************************************
*
* Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
*
******************************************************************************/
#define _RTW_EFUSE_C_
#include <osdep_service.h>
#include <drv_types.h>
#include <rtw_efuse.h>
#include <usb_ops_linux.h>
#include <rtl8188e_hal.h>
#include <rtw_iol.h>
#define REG_EFUSE_CTRL 0x0030
#define EFUSE_CTRL REG_EFUSE_CTRL /* E-Fuse Control. */
enum{
VOLTAGE_V25 = 0x03,
LDOE25_SHIFT = 28,
};
/*
* When we want to enable write operation, we should change to pwr on state.
* When we stop write, we should switch to 500k mode and disable LDO 2.5V.
*/
void efuse_power_switch(struct adapter *pAdapter, u8 write, u8 pwrstate)
{
u8 tempval;
u16 tmpv16;
if (pwrstate) {
usb_write8(pAdapter, REG_EFUSE_ACCESS, EFUSE_ACCESS_ON);
/* 1.2V Power: From VDDON with Power Cut(0x0000h[15]), default valid */
tmpv16 = usb_read16(pAdapter, REG_SYS_ISO_CTRL);
if (!(tmpv16 & PWC_EV12V)) {
tmpv16 |= PWC_EV12V;
usb_write16(pAdapter, REG_SYS_ISO_CTRL, tmpv16);
}
/* Reset: 0x0000h[28], default valid */
tmpv16 = usb_read16(pAdapter, REG_SYS_FUNC_EN);
if (!(tmpv16 & FEN_ELDR)) {
tmpv16 |= FEN_ELDR;
usb_write16(pAdapter, REG_SYS_FUNC_EN, tmpv16);
}
/* Clock: Gated(0x0008h[5]) 8M(0x0008h[1]) clock from ANA, default valid */
tmpv16 = usb_read16(pAdapter, REG_SYS_CLKR);
if ((!(tmpv16 & LOADER_CLK_EN)) || (!(tmpv16 & ANA8M))) {
tmpv16 |= (LOADER_CLK_EN | ANA8M);
usb_write16(pAdapter, REG_SYS_CLKR, tmpv16);
}
if (write) {
/* Enable LDO 2.5V before read/write action */
tempval = usb_read8(pAdapter, EFUSE_TEST + 3);
tempval &= 0x0F;
tempval |= (VOLTAGE_V25 << 4);
usb_write8(pAdapter, EFUSE_TEST + 3, (tempval | 0x80));
}
} else {
usb_write8(pAdapter, REG_EFUSE_ACCESS, EFUSE_ACCESS_OFF);
if (write) {
/* Disable LDO 2.5V after read/write action */
tempval = usb_read8(pAdapter, EFUSE_TEST + 3);
usb_write8(pAdapter, EFUSE_TEST + 3, (tempval & 0x7F));
}
}
}
static void
efuse_phymap_to_logical(u8 *phymap, u16 _offset, u16 _size_byte, u8 *pbuf)
{
u8 *efuseTbl = NULL;
u8 rtemp8;
u16 eFuse_Addr = 0;
u8 offset, wren;
u16 i, j;
u16 **eFuseWord = NULL;
u16 efuse_utilized = 0;
u8 u1temp = 0;
void **tmp = NULL;
efuseTbl = kzalloc(EFUSE_MAP_LEN_88E, GFP_KERNEL);
if (!efuseTbl)
return;
tmp = kcalloc(EFUSE_MAX_SECTION_88E,
sizeof(void *) + EFUSE_MAX_WORD_UNIT * sizeof(u16),
GFP_KERNEL);
if (!tmp) {
DBG_88E("%s: alloc eFuseWord fail!\n", __func__);
goto eFuseWord_failed;
}
for (i = 0; i < EFUSE_MAX_SECTION_88E; i++)
tmp[i] = ((char *)(tmp + EFUSE_MAX_SECTION_88E)) + i * EFUSE_MAX_WORD_UNIT * sizeof(u16);
eFuseWord = (u16 **)tmp;
/* 0. Refresh efuse init map as all oxFF. */
for (i = 0; i < EFUSE_MAX_SECTION_88E; i++)
for (j = 0; j < EFUSE_MAX_WORD_UNIT; j++)
eFuseWord[i][j] = 0xFFFF;
/* */
/* 1. Read the first byte to check if efuse is empty!!! */
/* */
/* */
rtemp8 = *(phymap+eFuse_Addr);
if (rtemp8 != 0xFF) {
efuse_utilized++;
eFuse_Addr++;
} else {
DBG_88E("EFUSE is empty efuse_Addr-%d efuse_data =%x\n", eFuse_Addr, rtemp8);
goto exit;
}
/* */
/* 2. Read real efuse content. Filter PG header and every section data. */
/* */
while ((rtemp8 != 0xFF) && (eFuse_Addr < EFUSE_REAL_CONTENT_LEN_88E)) {
/* Check PG header for section num. */
if ((rtemp8 & 0x1F) == 0x0F) { /* extended header */
u1temp = (rtemp8 & 0xE0) >> 5;
rtemp8 = *(phymap+eFuse_Addr);
if ((rtemp8 & 0x0F) == 0x0F) {
eFuse_Addr++;
rtemp8 = *(phymap+eFuse_Addr);
if (rtemp8 != 0xFF && (eFuse_Addr < EFUSE_REAL_CONTENT_LEN_88E))
eFuse_Addr++;
continue;
} else {
offset = ((rtemp8 & 0xF0) >> 1) | u1temp;
wren =