[WCNCR00148023] misc: support efuse read/write/dump by shell command

[Description]
Add efuse read/write/dump by shell command

Support efuse read/write by iwpriv and efuse dump by procfs

rea/write command format:
Help menu
        Read:   "efuse read addr_hex"
        Write:  "efuse write addr_hex val_hex"

Command samples:

read (iwpriv)
  iwpriv wlan0 driver "efuse read 0x0"

write (iwpriv)
  iwpriv wlan0 driver "efuse write 0x0 0x68"

dump (procfs)
  cat /proc/net/wlan/efuse_dump > /tmp/efuse_dump.txt

Feature: misc
Change-Id: I5b635984755b985ae5c3794251bfed2f7a5d7102
CR-Id: WCNCR00148023
Signed-off-by: Deren Wu <deren.wu@mediatek.com>
diff --git a/include/wlan_oid.h b/include/wlan_oid.h
index f83f843..0f77fd9 100644
--- a/include/wlan_oid.h
+++ b/include/wlan_oid.h
@@ -141,6 +141,7 @@
 
 #define BT_PROFILE_PARAM_LEN        8
 
+#define EFUSE_ADDR_MAX  0x3BF	/* Based on EEPROM layout 20160120 */
 #if CFG_SUPPORT_BUFFER_MODE
 
 /* For MT7668 */
diff --git a/os/linux/gl_proc.c b/os/linux/gl_proc.c
index 177b297..3fcfab0 100644
--- a/os/linux/gl_proc.c
+++ b/os/linux/gl_proc.c
@@ -69,6 +69,7 @@
 *                    E X T E R N A L   R E F E R E N C E S
 ********************************************************************************
 */
+#include "precomp.h"
 #include "gl_os.h"
 #include "gl_kal.h"
 #include "debug.h"
@@ -93,6 +94,7 @@
 #define PROC_DBG_LEVEL_NAME                     "dbg_level"
 #define PROC_DRIVER_CMD                         "driver"
 #define PROC_CFG                                "cfg"
+#define PROC_EFUSE_DUMP                         "efuse_dump"
 
 
 
@@ -183,6 +185,76 @@
 #if WLAN_INCLUDE_PROC
 #if	CFG_SUPPORT_EASY_DEBUG
 
+static void *procEfuseDump_start(struct seq_file *s, loff_t *pos)
+{
+	static unsigned long counter;
+
+	if (*pos == 0)
+		counter = *pos; /* read file init */
+
+	if (counter >= EFUSE_ADDR_MAX)
+		return NULL;
+	return &counter;
+}
+static void *procEfuseDump_next(struct seq_file *s, void *v, loff_t *pos)
+{
+	unsigned long *tmp_v = (unsigned long *)v;
+
+	(*tmp_v) += EFUSE_BLOCK_SIZE;
+
+	if (*tmp_v >= EFUSE_ADDR_MAX)
+		return NULL;
+	return tmp_v;
+}
+static void procEfuseDump_stop(struct seq_file *s, void *v)
+{
+	/* nothing to do, we use a static value in start() */
+}
+static int procEfuseDump_show(struct seq_file *s, void *v)
+{
+	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
+	UINT_32 u4BufLen = 0;
+	P_GLUE_INFO_T prGlueInfo;
+	UINT_32 idx_addr, idx_value;
+	PARAM_CUSTOM_ACCESS_EFUSE_T rAccessEfuseInfo = {};
+
+	prGlueInfo = g_prGlueInfo_proc;
+
+#if  (CFG_EEPROM_PAGE_ACCESS == 1)
+	idx_addr = *(loff_t *)v;
+	rAccessEfuseInfo.u4Address = (idx_addr / EFUSE_BLOCK_SIZE) * EFUSE_BLOCK_SIZE;
+
+	rStatus = kalIoctl(prGlueInfo,
+		wlanoidQueryProcessAccessEfuseRead,
+		&rAccessEfuseInfo,
+		sizeof(PARAM_CUSTOM_ACCESS_EFUSE_T), TRUE, TRUE, TRUE, &u4BufLen);
+	if (rStatus != WLAN_STATUS_SUCCESS) {
+		seq_printf(s, "efuse read fail (0x%03X)\n", rAccessEfuseInfo.u4Address);
+		return 0;
+	}
+
+	for (idx_value = 0; idx_value < EFUSE_BLOCK_SIZE; idx_value++)
+		seq_printf(s, "0x%03X=0x%02X\n"
+					, rAccessEfuseInfo.u4Address+idx_value
+					, prGlueInfo->prAdapter->aucEepromVaule[idx_value]);
+	return 0;
+#else
+	seq_puts(s, "efuse ops is invalid\n");
+	return -EPERM; /* return negative value to stop read process */
+#endif
+}
+static int procEfuseDumpOpen(struct inode *inode, struct file *file)
+{
+	static const struct seq_operations procEfuseDump_ops = {
+		.start = procEfuseDump_start,
+		.next  = procEfuseDump_next,
+		.stop  = procEfuseDump_stop,
+		.show  = procEfuseDump_show
+	};
+
+	return seq_open(file, &procEfuseDump_ops);
+}
+
 
 static ssize_t procCfgRead(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
 {
@@ -425,6 +497,14 @@
 #if WLAN_INCLUDE_PROC
 #if	CFG_SUPPORT_EASY_DEBUG
 
+static const struct file_operations efusedump_ops = {
+	.owner	 = THIS_MODULE,
+	.open	 = procEfuseDumpOpen,
+	.read	 = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
 static const struct file_operations drivercmd_ops = {
 	.owner = THIS_MODULE,
 	.read = procDriverCmdRead,
@@ -741,6 +821,7 @@
 	remove_proc_entry(PROC_DRIVER_CMD, gprProcRoot);
 	remove_proc_entry(PROC_DBG_LEVEL_NAME, gprProcRoot);
 	remove_proc_entry(PROC_CFG, gprProcRoot);
+	remove_proc_entry(PROC_EFUSE_DUMP, gprProcRoot);
 
 #if CFG_SUPPORT_DEBUG_FS
 	remove_proc_entry(PROC_ROAM_PARAM, gprProcRoot);
@@ -787,6 +868,12 @@
 		pr_err("Unable to create /proc entry for driver command\n\r");
 		return -1;
 	}
+
+	prEntry = proc_create(PROC_EFUSE_DUMP, 0664, gprProcRoot, &efusedump_ops);
+	if (prEntry == NULL) {
+		pr_err("Unable to create /proc entry efuse\n\r");
+		return -1;
+	}
 #endif
 
 	prEntry = proc_create(PROC_DBG_LEVEL_NAME, 0664, gprProcRoot, &dbglevel_ops);
diff --git a/os/linux/gl_wext_priv.c b/os/linux/gl_wext_priv.c
index 96bae3c..48f8482 100644
--- a/os/linux/gl_wext_priv.c
+++ b/os/linux/gl_wext_priv.c
@@ -2247,6 +2247,8 @@
 #define CMD_GET_CH_RANK_LIST "GET_CH_RANK_LIST"
 #endif
 
+#define CMD_EFUSE		"EFUSE"
+
 /* miracast related definition */
 #define MIRACAST_MODE_OFF	0
 #define MIRACAST_MODE_SOURCE	1
@@ -7852,6 +7854,112 @@
 }
 #endif
 
+static int priv_driver_efuse_ops(IN struct net_device *prNetDev, IN char *pcCommand, IN int i4TotalLen)
+{
+	enum EFUSE_OP_MODE {
+		EFUSE_READ,
+		EFUSE_WRITE,
+		EFUSE_INVALID,
+	};
+	UINT_8 ucOpMode = EFUSE_INVALID;
+	UCHAR ucOpChar;
+	INT_32 i4Argc = 0;
+	PCHAR apcArgv[WLAN_CFG_ARGV_MAX];
+	UINT_32 u4Ret;
+	INT_32 i4Parameter;
+	UINT_32 u4Efuse_addr = 0;
+	UINT_8 ucEfuse_value = 0;
+
+#if  (CFG_EEPROM_PAGE_ACCESS == 1)
+	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
+	UINT_32 u4Offset = 0;
+	UINT_32 u4BufLen = 0;
+	UINT_8  u4Index = 0;
+	P_GLUE_INFO_T prGlueInfo = NULL;
+	PARAM_CUSTOM_ACCESS_EFUSE_T rAccessEfuseInfo;
+#endif
+	wlanCfgParseArgument(pcCommand, &i4Argc, apcArgv);
+
+	/* Sanity check */
+	if (i4Argc < 2)
+		goto efuse_op_invalid;
+
+	ucOpChar = (UCHAR)apcArgv[1][0];
+	if ((i4Argc == 3) && (ucOpChar == 'r' || ucOpChar == 'R'))
+		ucOpMode = EFUSE_READ;
+	else if ((i4Argc == 4) && (ucOpChar == 'w' || ucOpChar == 'W'))
+		ucOpMode = EFUSE_WRITE;
+
+	/* Print out help if input format is wrong */
+	if (ucOpMode == EFUSE_INVALID)
+		goto efuse_op_invalid;
+
+	/* convert address */
+	if (ucOpMode == EFUSE_READ || ucOpMode == EFUSE_WRITE) {
+		u4Ret = kalkStrtos32(apcArgv[2], 16, &i4Parameter);
+		u4Efuse_addr = (UINT_32)i4Parameter;
+	}
+
+	/* convert value */
+	if (ucOpMode == EFUSE_WRITE) {
+		u4Ret = kalkStrtos32(apcArgv[3], 16, &i4Parameter);
+		ucEfuse_value = (UINT_8)i4Parameter;
+	}
+
+	/* Start operation */
+#if  (CFG_EEPROM_PAGE_ACCESS == 1)
+	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));
+	kalMemSet(&rAccessEfuseInfo, 0, sizeof(PARAM_CUSTOM_ACCESS_EFUSE_T));
+	rAccessEfuseInfo.u4Address = (u4Efuse_addr / EFUSE_BLOCK_SIZE) * EFUSE_BLOCK_SIZE;
+	u4Index = u4Efuse_addr % EFUSE_BLOCK_SIZE;
+
+	if (ucOpMode == EFUSE_READ) {
+		rStatus = kalIoctl(prGlueInfo,
+					wlanoidQueryProcessAccessEfuseRead,
+					&rAccessEfuseInfo,
+					sizeof(PARAM_CUSTOM_ACCESS_EFUSE_T), TRUE, TRUE, TRUE, &u4BufLen);
+
+		if (rStatus == WLAN_STATUS_SUCCESS) {
+			u4Offset += snprintf(pcCommand + u4Offset, i4TotalLen - u4Offset,
+						"Read success 0x%X = 0x%X\n", u4Efuse_addr
+						, prGlueInfo->prAdapter->aucEepromVaule[u4Index]);
+		}
+	} else if (ucOpMode == EFUSE_WRITE) {
+
+		prGlueInfo->prAdapter->aucEepromVaule[u4Index] = ucEfuse_value;
+
+		kalMemCopy(rAccessEfuseInfo.aucData, prGlueInfo->prAdapter->aucEepromVaule, 16);
+
+		rStatus = kalIoctl(prGlueInfo,
+					wlanoidQueryProcessAccessEfuseWrite,
+					&rAccessEfuseInfo,
+					sizeof(PARAM_CUSTOM_ACCESS_EFUSE_T), FALSE, FALSE, TRUE, &u4BufLen);
+		if (rStatus == WLAN_STATUS_SUCCESS) {
+			u4Offset += snprintf(pcCommand + u4Offset, i4TotalLen - u4Offset,
+						"Write success 0x%X = 0x%X\n"
+						, u4Efuse_addr
+						, ucEfuse_value);
+		}
+	}
+#else
+	u4Offset += snprintf(pcCommand + u4Offset, i4TotalLen - u4Offset,
+					"efuse ops is invalid\n");
+#endif
+
+	return (INT_32)u4Offset;
+
+efuse_op_invalid:
+
+	u4Offset += snprintf(pcCommand + u4Offset, i4TotalLen - u4Offset,
+				"\nHelp menu\n");
+	u4Offset += snprintf(pcCommand + u4Offset, i4TotalLen - u4Offset,
+				"\tRead:\t\"efuse read addr_hex\"\n");
+	u4Offset += snprintf(pcCommand + u4Offset, i4TotalLen - u4Offset,
+				"\tWrite:\t\"efuse write addr_hex val_hex\"\n");
+	return (INT_32)u4Offset;
+}
+
+
 INT_32 priv_driver_cmds(IN struct net_device *prNetDev, IN PCHAR pcCommand, IN INT_32 i4TotalLen)
 {
 	P_GLUE_INFO_T prGlueInfo = NULL;
@@ -8065,6 +8173,8 @@
 		else if (strnicmp(pcCommand, CMD_GET_CH_RANK_LIST, strlen(CMD_GET_CH_RANK_LIST)) == 0)
 			i4BytesWritten = priv_driver_get_ch_rank_list(prNetDev, pcCommand, i4TotalLen);
 #endif
+		else if (strnicmp(pcCommand, CMD_EFUSE, sizeof(CMD_EFUSE)-1) == 0)
+			i4BytesWritten = priv_driver_efuse_ops(prNetDev, pcCommand, i4TotalLen);
 		else
 			i4CmdFound = 0;
 	}