/*++
WM8880 eFuse char device driver
Copyright (c) 2013 - 2014 WonderMedia Technologies, Inc.
This program is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software Foundation,
either version 2 of the License, or (at your option) any later version.
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, see .
WonderMedia Technologies, Inc.
2013-11-11, HowayHuo, ShenZhen
--*/
/*--- History -------------------------------------------------------------------
* DATE | AUTHORS | DESCRIPTION
* 2013/11/11 Howay Huo v1.0, First Release
*
*
*------------------------------------------------------------------------------*/
/*---------------------------- WM8880 eFuse Layout -------------------------------------------------------
Type StartAddr DataBytes HammingECCBytes OccupyBytes
Hardware Reserved 0 8 0 8
Bounding 8 6 2 8
CPUID 16 8 3 12
UUID 28 24 8 32
Unused Space 60 51 17 68
Explain:
OccupyBytes = 3aligned(DataBytes) + HammingECCBytes
eFuse Total 128 Bytes = 8 HardwareReservedBytes + 90 DataBytes + 30 ECCBytes
1 Block = 4 Bytes = 3 DataBytes + 1 ECCByte
The minimum unit of eFuse_ECC_Read/Write is 1 Block (4 Bytes)
eFuse Total 32 Block = 2 HardwareReservedBlocks + 30 AvailableBlock
---------------------------------------------------------------------------------------------------------*/
//Control files: /sys/class/efuse/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "wmt_efuse.h"
/*********************************** Constant Macro **********************************/
#define EFUSE_NAME "efuse"
#define EFUSE_AVAILABLE_START_BYTE 8
#define EFUSE_AVAILABLE_BYTES_NUM ((128 - EFUSE_AVAILABLE_START_BYTE) / 4 * 3) // (120 / 4) * 3
/*
* Note: Max bytes number should be 3 aligned
*/
#define EFUSE_BOUND_MAX_BYTES_NUM 6
#define EFUSE_CPUID_MAX_BYTES_NUM 8
#define EFUSE_UUID_MAX_BYTES_NUM 24
//#define EFUSE_WRITE_THROUGH_SYSFS
/*
* For example: setenv wmt.efuse.gpio 14:1
* It means pull high gpio14 to enable efuse write
*/
#define ENV_EFUSE_GPIO "wmt.efuse.gpio"
/**************************** Data type and local variable ***************************/
/*
* WM8880 VDD25EFUSE pin is controlled by WM8880 GPIO via a MOS.
* Pull High VDD25EFUSE pin to program eFuse. Pull Low VDD25EFUSE pin to read eFuse.
*/
static VDD25_GPIO vdd25_control_pin = {WMT_PIN_GP1_GPIO14, 1};
static int vdd25_control;
static const char *otp_type_str[] = {
"bound",
"cpuid",
"uuid",
};
static int efuse_major;
static struct device *p_efuse_device;
/********************************** Function declare *********************************/
#undef DEBUG
//#define DEBUG //if you need see the debug info, please define it.
#undef DBG
#ifdef DEBUG
#define DBG(fmt, args...) printk(KERN_INFO "[" EFUSE_NAME "] " fmt , ## args)
#else
#define DBG(fmt, args...)
#endif
#define INFO(fmt, args...) printk(KERN_INFO "[" EFUSE_NAME "] " fmt , ## args)
#define ERROR(fmt,args...) printk(KERN_ERR "[" EFUSE_NAME "]Error: " fmt , ## args)
#define WARNING(fmt,args...) printk(KERN_WARNING "[" EFUSE_NAME "]Warning: " fmt , ## args)
extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
/******************************** Function implement *********************************/
static void int_to_bin(unsigned int data, unsigned char *binary, int len)
{
int i;
for (i = 0; i < len; i++) {
binary[i] = data % 2;
data = (data >> 1);
}
}
static int bin_to_int(unsigned char *binary, unsigned int *p_data, int len)
{
int i;
unsigned char data = 0, *p;
p = (unsigned char *)p_data;
for(i = 0; i < len; i++) {
if(binary[i] != 0 && binary[i] != 1) {
ERROR("wrong binary[%d] = 0x%02X\n", i, binary[i]);
return -1;
}
data += (binary[i] << (i % 8));
//printk("binary[%d] = %d, data = %d\n", i, binary[i], data);
if((i + 1) % 8 == 0) {
*p = data;
p++;
data = 0;
}
}
return 0;
}
/* matrix Inner product */
static unsigned char hamming_check(unsigned int data, unsigned int parity, int len)
{
unsigned char *dst;
unsigned char *src;
int i, count = 0;
dst = kzalloc(len, GFP_KERNEL);
src = kzalloc(len, GFP_KERNEL);
int_to_bin(data, dst, len);
int_to_bin(parity, src, len);
for (i = 0; i < len; i++) {
if (dst[i] & src[i])
count++;
}
kfree(dst);
kfree(src);
if (count % 2)
return 1;
else
return 0;
}
static int hamming_encode_31_26(unsigned int data, unsigned int *p_hamming_data)
{
int i, ret;
unsigned int parity_num[5] = {0x2AAAD5B, 0x333366D, 0x3C3C78E, 0x3FC07F0, 0x3FFF800};
unsigned int tmp;
unsigned char encode_data[32];
int len;
char hamming_binary_buf[32];
tmp = data;
for (i = 0; i < 31; i++) {
if (i == 0)
encode_data[0] = hamming_check(data, parity_num[0], 26);
else if (i == 1)
encode_data[1] = hamming_check(data, parity_num[1], 26);
else if (i == 3)
encode_data[3] = hamming_check(data, parity_num[2], 26);
else if (i == 7)
encode_data[7] = hamming_check(data, parity_num[3], 26);
else if (i == 15)
encode_data[15] = hamming_check(data, parity_num[4], 26);
else {
if ((tmp << 31) >> 31)
encode_data[i] = 1;
else
encode_data[i] = 0;
tmp >>= 1;
}
}
encode_data[31] = 0;
len = 0;
for(i = 30; i >= 0; i--) {
if(encode_data[i] != 0 && encode_data[i] != 1) {
ERROR("wrong hamming_encode_31_26, encode_data[%d] = 0x%02X\n", i, encode_data[i]);
return -1;
}
len += sprintf(hamming_binary_buf + len, "%d", encode_data[i]);
}
hamming_binary_buf[31] = 0;
ret = bin_to_int(encode_data, p_hamming_data, 32);
if(ret)
return -1;
INFO("hamming_encode_31_26: 0x%08X ==> 0x%08X (%s)\n",
data, *p_hamming_data, hamming_binary_buf);
return 0;
}
static void efuse_vdd25_active(int active)
{
if(vdd25_control == 0)
return;
if(active)
gpio_direction_output(vdd25_control_pin.gpiono, vdd25_control_pin.active ? 1 : 0);
else
gpio_direction_output(vdd25_control_pin.gpiono, vdd25_control_pin.active ? 0 : 1);
}
static void __efuse_read_ready(void)
{
unsigned int mode;
unsigned int val = 0;
/* VDD25 set low */
efuse_vdd25_active(0);
/* set idle*/
mode = EFUSE_MODE_VAL;
mode = (mode & 0xFFFFFFFC) | EFUSE_MODE_IDLE;
EFUSE_MODE_VAL = mode;
/* CSB set high */
val |= EFUSE_DIR_CSB;
/* VDDQ set high */
val |= EFUSE_DIR_VDDQ;
/* PGENB set low */
val &= ~EFUSE_DIR_PGENB;
/* STROBE set low */
val &= ~EFUSE_DIR_STROBE;
/* LOAD set low */
val &= ~EFUSE_DIR_LOAD;
EFUSE_DIR_CMD_VAL = val;
DBG("__efuse_read_ready: vdd25 = %d, mode = 0x%08x, cmd = 0x%08x",
vdd25_control ? gpio_get_value(vdd25_control_pin.gpiono) : -1, EFUSE_MODE_VAL, EFUSE_DIR_CMD_VAL);
udelay(1);
}
static void __efuse_read_init(void)
{
unsigned int mode;
__efuse_read_ready();
/* set DirectAccess */
mode = EFUSE_MODE_VAL;
mode = (mode & 0xFFFFFFFC) | EFUSE_MODE_DA;
EFUSE_MODE_VAL = mode;
/* VDD25 set low */
efuse_vdd25_active(0);
/* VDDQ set low */
EFUSE_DIR_CMD_VAL &= ~EFUSE_DIR_VDDQ;
udelay(1);
/* CSB set low */
EFUSE_DIR_CMD_VAL &= ~EFUSE_DIR_CSB;
udelay(1);
/* PGENB set high */
EFUSE_DIR_CMD_VAL |= EFUSE_DIR_PGENB;
udelay(1);
/* LOAD set high */
EFUSE_DIR_CMD_VAL |= EFUSE_DIR_LOAD;
udelay(1);
/* STROBE set low */
//EFUSE_DIR_CMD_VAL &= ~EFUSE_DIR_STROBE;
DBG("__efuse_read_init: vdd25 = %d, mode = 0x%08x, cmd = 0x%08x",
vdd25_control ? gpio_get_value(vdd25_control_pin.gpiono) : -1, EFUSE_MODE_VAL, EFUSE_DIR_CMD_VAL);
}
static void __efuse_read_exit(void)
{
unsigned int mode;
/* LOAD set low */
EFUSE_DIR_CMD_VAL &= ~EFUSE_DIR_LOAD;
udelay(1);
/* PGENB set low */
EFUSE_DIR_CMD_VAL &= ~EFUSE_DIR_PGENB;
udelay(1);
/* CSB set high */
EFUSE_DIR_CMD_VAL |= EFUSE_DIR_CSB;
udelay(1);
/* VDDQ set high */
EFUSE_DIR_CMD_VAL |= EFUSE_DIR_VDDQ;
udelay(1);
/* VDD25 set low */
efuse_vdd25_active(0);
/* set idle*/
mode = EFUSE_MODE_VAL;
mode = (mode & 0xFFFFFFFC) | EFUSE_MODE_IDLE;
EFUSE_MODE_VAL = mode;
udelay(1);
/* Standby mode */
/* VDD25 set low */
efuse_vdd25_active(0);
/* VDDQ set low */
EFUSE_DIR_CMD_VAL &= ~EFUSE_DIR_VDDQ;
/* PGENB set high */
EFUSE_DIR_CMD_VAL |= EFUSE_DIR_PGENB;
/* LOAD set high */
EFUSE_DIR_CMD_VAL |= EFUSE_DIR_LOAD;
DBG("__efuse_read_exit\n");
}
static void print_write_read_bytes(int have_title, unsigned char *p_wbuf, unsigned char *p_rbuf, int count)
{
int i;
int len;
char *pbuf;
unsigned char *p;
/* print Write bytes */
if(p_wbuf != NULL) {
len = 0;
p = p_wbuf;
pbuf = (unsigned char *)kzalloc(count * 6, GFP_KERNEL);
for(i = 0; i < count; i++) {
if(have_title) {
if(i % 16 == 0)
len += sprintf(pbuf + len, "\n");
} else {
if(i != 0 && i % 16 == 0)
len += sprintf(pbuf + len, "\n");
}
if(i != count - 1)
len += sprintf(pbuf + len, "0x%02x,", *(p + (count - 1 - i)));
else
len += sprintf(pbuf + len, "0x%02x\n", *(p + (count - 1 - i)));
}
if(have_title)
INFO("write bytes: %s", pbuf);
else
INFO("%s", pbuf);
kfree(pbuf);
}
/* print Read bytes */
if(p_rbuf != NULL) {
len = 0;
p = p_rbuf;
pbuf = (unsigned char *)kzalloc(count * 6, GFP_KERNEL);
for(i = 0; i < count; i++) {
if(have_title) {
if(i % 16 == 0)
len += sprintf(pbuf + len, "\n");
} else {
if(i != 0 && i % 16 == 0)
len += sprintf(pbuf + len, "\n");
}
if(i != count - 1)
len += sprintf(pbuf + len, "0x%02x,", *(p + (count - 1 - i)));
else
len += sprintf(pbuf + len, "0x%02x\n", *(p + (count - 1 - i)));
}
if(have_title)
INFO("read bytes: %s", pbuf);
else
INFO("%s", pbuf);
kfree(pbuf);
}
}
static void print_write_read_integers(int have_title, unsigned int *p_wbuf, unsigned int *p_rbuf, int count)
{
int i;
int len;
char *pbuf;
unsigned int *p;
/* print Write integers */
if(p_wbuf != NULL) {
len = 0;
p = p_wbuf;
pbuf = (unsigned char *)kzalloc(count * 12, GFP_KERNEL);
for(i = 0; i < count; i++) {
if(have_title) {
if(i % 4 == 0)
len += sprintf(pbuf + len, "\n");
} else {
if(i != 0 && i % 4 == 0)
len += sprintf(pbuf + len, "\n");
}
if(i != count - 1)
len += sprintf(pbuf + len, "0x%08x,", *(p + (count - 1 - i)));
else
len += sprintf(pbuf + len, "0x%08x\n", *(p + (count - 1 - i)));
}
if(have_title)
INFO("write integers: %s", pbuf);
else
INFO("%s", pbuf);
kfree(pbuf);
}
/* print Read integers */
if(p_rbuf != NULL) {
len = 0;
p = p_rbuf;
pbuf = (unsigned char *)kzalloc(count * 12, GFP_KERNEL);
for(i = 0; i < count; i++) {
if(have_title) {
if(i % 4 == 0)
len += sprintf(pbuf + len, "\n");
} else {
if(i != 0 && i % 4 == 0)
len += sprintf(pbuf + len, "\n");
}
if(i != count - 1)
len += sprintf(pbuf + len, "0x%08x,", *(p + (count - 1 - i)));
else
len += sprintf(pbuf + len, "0x%08x\n", *(p + (count - 1 - i)));
}
if(have_title)
INFO("read integers: %s", pbuf);
else
INFO("%s", pbuf);
kfree(pbuf);
}
}
static int efuse_read_bytes(int start, unsigned char *p_read_bytes, int count)
{
int i, read_count;
unsigned char *p;
memset(p_read_bytes, 0, count);
if(start > 127 || start < 0) {
ERROR("efuse_read_bytes: start = %d is invalid, it should be 0 ~ 127\n", start);
return -1;
}
if(start + count > 128)
read_count = 128 - start;
else
read_count = count;
__efuse_read_init();
p = p_read_bytes;
for(i = start; i < start + read_count; i++) {
/* set read address */
EFUSE_ADR_VAL = i;
/* STROBE set high to read data */
EFUSE_DIR_CMD_VAL |= EFUSE_DIR_STROBE;
udelay(1);
/* STROBE set low */
EFUSE_DIR_CMD_VAL &= ~EFUSE_DIR_STROBE;
udelay(1);
/* read data */
*p = EFUSE_RD_DATA_VAL & 0xFF;
p++;
}
__efuse_read_exit();
#ifdef DEBUG
print_write_read_bytes(1, NULL, p_read_bytes, count);
#endif
if(read_count != count) {
ERROR("Need read %d bytes. In fact read %d bytes\n", count, read_count);
print_write_read_bytes(1, NULL, p_read_bytes, count);
}
return read_count;
}
static void __efuse_write_ready(void)
{
unsigned int mode;
unsigned int val = 0;
/* VDD25 set low */
efuse_vdd25_active(0);
/* set idle*/
mode = EFUSE_MODE_VAL;
mode = (mode & 0xFFFFFFFC) | EFUSE_MODE_IDLE;
EFUSE_MODE_VAL = mode;
/* CSB set high */
val |= EFUSE_DIR_CSB;
/* VDDQ set low */
val &= ~EFUSE_DIR_VDDQ;
/* PGENB set high */
val |= EFUSE_DIR_PGENB;
/* STROBE set low */
val &= ~EFUSE_DIR_STROBE;
/* LOAD set high */
val |= EFUSE_DIR_LOAD;
EFUSE_DIR_CMD_VAL = val;
DBG("__efuse_write_ready: vdd25 = %d, mode = 0x%08x, cmd = 0x%08x",
vdd25_control ? gpio_get_value(vdd25_control_pin.gpiono) : -1, EFUSE_MODE_VAL, EFUSE_DIR_CMD_VAL);
udelay(1);
}
static void __efuse_write_init(void)
{
unsigned int mode;
__efuse_write_ready();
/* set DirectAccess */
mode = EFUSE_MODE_VAL;
mode = (mode & 0xFFFFFFFC) | EFUSE_MODE_DA;
EFUSE_MODE_VAL = mode;
/* PGENB set low */
EFUSE_DIR_CMD_VAL &= ~EFUSE_DIR_PGENB;
udelay(1);
/* VDD25 set high */
efuse_vdd25_active(1);
/* VDDQ set high*/
EFUSE_DIR_CMD_VAL |= EFUSE_DIR_VDDQ;
udelay(1);
/* LOAD set low */
EFUSE_DIR_CMD_VAL &= ~EFUSE_DIR_LOAD;
udelay(1);
/* CSB set low */
EFUSE_DIR_CMD_VAL &= ~EFUSE_DIR_CSB;
udelay(1);
DBG("__efuse_write_init: vdd25 = %d, mode = 0x%08x, cmd = 0x%08x",
vdd25_control ? gpio_get_value(vdd25_control_pin.gpiono) : -1, EFUSE_MODE_VAL, EFUSE_DIR_CMD_VAL);
}
static void __efuse_write_exit(void)
{
int mode;
/* CSB set high */
EFUSE_DIR_CMD_VAL |= EFUSE_DIR_CSB;
udelay(1);
/* LOAD set high */
EFUSE_DIR_CMD_VAL |= EFUSE_DIR_LOAD;
udelay(1);
/* VDD25 set low */
efuse_vdd25_active(0);
/* VDDQ set low */
EFUSE_DIR_CMD_VAL &= ~EFUSE_DIR_VDDQ;
udelay(1);
/* PGENB set high */
EFUSE_DIR_CMD_VAL |= EFUSE_DIR_PGENB;
udelay(1);
/* set idle*/
mode = EFUSE_MODE_VAL;
mode = (mode & 0xFFFFFFFC) | EFUSE_MODE_IDLE;
EFUSE_MODE_VAL = mode;
DBG("__efuse_write_exit\n");
}
static int efuse_write_bytes(int start, unsigned char *p_write_bytes, int count)
{
int i, j;
int write_addr;
unsigned char binary[8];
unsigned char *p, *pbuf;
if(start < 8 || start > 127) {
ERROR("efuse_write_bytes: start = %d is invalid, it should be 8 ~ 127\n", start);
return -1;
}
if(start + count > 128) {
ERROR("efuse_write_bytes: byte_no range[%d - %d], it should be in range[0 - 127]\n",
start, start + count -1);
return -1;
}
pbuf = (unsigned char *)kzalloc(count, GFP_KERNEL);
if(pbuf == NULL) {
ERROR("efuse_write_bytes: kzalloc buf fail\n");
return -2;
}
DBG("========== read byte before write ==========\n");
efuse_read_bytes(start, pbuf, count);
for(i = 0; i < count; i++) {
if(*(pbuf + i) != 0) {
ERROR("eFuse has been programmed. Byte%d = 0x%02X. Exit.\n",
start + i, *(pbuf + i));
print_write_read_bytes(1, NULL, pbuf, count);
kfree(pbuf);
return -3;
}
}
DBG("========== start write efuse ==========\n");
p = p_write_bytes;
__efuse_write_init();
for(i = start; i < start + count; i++) {
int_to_bin(*p, binary, 8);
DBG("binary: %d%d%d%d%d%d%d%d\n", binary[7], binary[6], binary[5], binary[4],
binary[3], binary[2], binary[1], binary[0]);
for(j = 0; j < 8; j++) {
if(binary[j]) {
/* */
write_addr = ((j << 7) | i );
/* address */
EFUSE_ADR_VAL = write_addr;
/* STROBE set high to write data */
EFUSE_DIR_CMD_VAL |= EFUSE_DIR_STROBE;
/* wait 12us */
udelay(12);
/* STROBE set low */
EFUSE_DIR_CMD_VAL &= ~EFUSE_DIR_STROBE;
udelay(1);
}
}
p++;
}
__efuse_write_exit();
DBG("========== read byte after write ==========\n");
efuse_read_bytes(start, pbuf, count);
p = p_write_bytes;
for(i = 0; i < count; i++) {
if(*(pbuf + i) != *(p + i)) {
ERROR("=========> eFuse Write Failed !!!\n");
ERROR("efuse_write_bytes: Byte%d wrtie 0x%02X, But read back 0x%02X\n",
start + i, *(p + i), *(pbuf + i));
print_write_read_bytes(1, p_write_bytes, pbuf, count);
kfree(pbuf);
return -4;
}
}
DBG("=========> eFuse Write Success !!!\n");
#ifdef DEBUG
print_write_read_bytes(1, p_write_bytes, pbuf, count);
#endif
kfree(pbuf);
return count;
}
static int efuse_hamming_write_int(int start, unsigned int *p_write_int, int count)
{
int i, ret;
unsigned int *p_hamming_data;
unsigned char *p_write_bytes;
if(start > 127 || start < 8) {
ERROR("efuse_hamming_write_int: invalid start = %d, start should be 8 ~ 127\n", start);
return -1;
}
if(start % 4 != 0) {
ERROR("efuse_hamming_write_int: invalid start = %d, start should be 4 align\n", start);
return -1;
}
if(count < 1) {
ERROR("efuse_hamming_write_int: count = %d, it must >= 1\n", count);
return -1;
}
if((start + count * 4) > 128) {
ERROR("efuse_hamming_write_int: start + count * 4 = %d, it shoud <= 128\n", start + count * 4);
return -1;
}
p_hamming_data = (unsigned int *)kzalloc(count * 4, GFP_KERNEL);
if (p_hamming_data == NULL) {
ERROR("efuse_hamming_write_int: kzalloc fail\n");
return -1;
}
for(i = 0; i < count; i++) {
ret = hamming_encode_31_26(*(p_write_int + i), p_hamming_data + i);
if(ret) {
kfree(p_hamming_data);
return -1;
}
/*
{
unsigned int tmp;
tmp = *(p_hamming_data + i);
*(p_hamming_data + i) &= 0xFF0FFFFF;
INFO("Modify: 0x%08X ==> 0x%08X\n", tmp, *(p_hamming_data + i));
}
*/
}
INFO("write %d hamming data:\n", count);
print_write_read_integers(0, p_hamming_data, NULL, count);
p_write_bytes = (unsigned char *)p_hamming_data;
ret = efuse_write_bytes(start, p_write_bytes, count * 4);
if(ret != count * 4) {
kfree(p_hamming_data);
return -1;
}
kfree(p_hamming_data);
INFO("hamming write Byte%d ~ Byte%d (%d Bytes) success\n", start, start + count * 4 - 1, count * 4);
return count;
}
static int efuse_hamming_read_int(int start, unsigned int *p_read_int, unsigned char *p_ecc_error, int count)
{
unsigned int mode;
int i, index;
const int timeout = 500;
int read_count = count;
if(start > 127 || start < 8) {
ERROR("efuse_hamming_read_int: start = %d is invalid, it should be 8 ~ 127\n", start);
return -1;
}
if(start % 4 != 0) {
ERROR("efuse_hamming_read_int: invalid byte_no = %d, byte_no should be 4 aligned\n", start);
return -1;
}
if(read_count < 1) {
ERROR("efuse_hamming_read_int: read_count = %d, it must >= 1\n", read_count);
return -1;
}
if((start + read_count * 4) > 128) {
WARNING("efuse_hamming_read_int: start + read_count * 4 = %d, it shoud <= 128\n", start + read_count * 4);
while ((start + read_count * 4) > 128)
read_count--;
}
/* VDD25 set low */
efuse_vdd25_active(0);
udelay(1);
/* set idle*/
mode = EFUSE_MODE_VAL;
mode = (mode & 0xFFFFFFFC) | EFUSE_MODE_IDLE;
EFUSE_MODE_VAL = mode;
udelay(1);
/* Make sure EFUSE_MODE[1:0] = 2'b00 */
i = 0;
while(i < timeout) {
if((EFUSE_MODE_VAL & 0xFF) == EFUSE_MODE_IDLE)
break;
i++;
INFO("efuse_hamming_read_int: wait %d ms for EFUSE_MODE idle\n", i);
msleep(1);
}
if(i == timeout) {
ERROR("efuse_hamming_read_int fail, EFUSE_MODE couldn't be set to IDLE.\n");
return -1;
}
/* Program EFUSE_MODE[30] = 1'b1 */
EFUSE_MODE_VAL |= EFUSE_ECC_EN;
udelay(1);
/* Program EFUSE_MODE[31] = 1'b1 */
EFUSE_MODE_VAL |= EFUSE_ECC_READ;
udelay(1);
/* Wait EFUSE_MODE[31] return back to 1'b0 */
i = 0;
while(i < timeout) {
if((EFUSE_MODE_VAL & EFUSE_ECC_READ) == 0)
break;
i++;
msleep(1);
INFO("efuse_hamming_read_int: wait %d ms for read completed\n", i);
}
if(i == timeout) {
ERROR("efuse_hamming_read_int timeout\n");
return -1;
}
INFO("EFUSE_ECC_STATUS_0_VAL = 0x%08X\n", EFUSE_ECC_STATUS_0_VAL);
INFO("EFUSE_ECC_STATUS_1_VAL = 0x%08X\n", EFUSE_ECC_STATUS_1_VAL);
index = start / 4 - 2;
for(i = 0; i < read_count; i++) {
if((EFUSE_ECC_STATUS_0_VAL >> index) & 0x01) {
WARNING("ECC Error Detected in Addr %d ~ Addr %d\n", (index + 2) * 4, (index + 2) * 4 + 3);
*(p_ecc_error + i) = 1;
} else
*(p_ecc_error + i) = 0;
/* Program EFUSE_ECCSRAM_ADR. */
EFUSE_ECCSRAM_ADR_VAL = (start + i * 4 - 8) / 4;
/* Note: Program EFUSE_ECCSRAM_ADR again. Otherwise, the EFUSE_ECCSRAM_RDPORT_VAL isn't updated */
EFUSE_ECCSRAM_ADR_VAL = (start + i * 4 - 8) / 4;
udelay(1);
/* Read the EFUSE_ECCSRAM_RDPORT to retrieve the ECCSRAM content */
*(p_read_int + i) = EFUSE_ECCSRAM_RDPORT_VAL & 0x3FFFFFF;
index++;
}
if(read_count != count) {
ERROR("Need read %d bytes. In fact read %d bytes\n", count, read_count);
print_write_read_integers(1, NULL, p_read_int, count);
}
return read_count;
}
static int bytes_3aligned(int bytes_num)
{
int aligned_bytes;
// 3 aligned
if(bytes_num % 3)
aligned_bytes = bytes_num + (3 - bytes_num % 3);
else
aligned_bytes = bytes_num;
return aligned_bytes;
}
static int caculate_need_bytes(int available_bytes)
{
int need_bytes;
// 4 aligned
need_bytes = bytes_3aligned(available_bytes) / 3 * 4;
//INFO("caculate_need_bytes = %d\n", need_bytes);
return need_bytes;
}
/*------------------------------------------------------------------------------
*
* Function: efuse_write_otp()
* Param:
* type: CPUID,UUID,...,etc
* wbuf: write bytes
* wlen: how many bytes need to write
* Return:
* return the write bytes number
* If the write bytes number is equal to the excepted bytes number, success.
* Otherwise, fail
*
*------------------------------------------------------------------------------*/
int efuse_write_otp(OTP_TYPE type, unsigned char *wbuf, int wlen)
{
int i, ret, start, byte_len, int_len, max_byte_len;
unsigned char *tmpbuf;
unsigned int *p_write_int;
// data length should be 3 aligned
byte_len = bytes_3aligned(wlen);
int_len = byte_len / 3;
switch(type) {
case OTP_CPUID:
max_byte_len = EFUSE_CPUID_MAX_BYTES_NUM;
start = EFUSE_AVAILABLE_START_BYTE + caculate_need_bytes(EFUSE_BOUND_MAX_BYTES_NUM);
break;
case OTP_UUID:
max_byte_len = EFUSE_UUID_MAX_BYTES_NUM;
start = EFUSE_AVAILABLE_START_BYTE + caculate_need_bytes(EFUSE_BOUND_MAX_BYTES_NUM)
+ caculate_need_bytes(EFUSE_CPUID_MAX_BYTES_NUM);
break;
default:
ERROR("efuse_write_otp: Not support otp type %s\n", otp_type_str[type]);
return -1;
}
if(wlen > max_byte_len) {
ERROR("efuse_write_otp(type: %s): length = %d, it should be <= %d\n",
otp_type_str[type], wlen, max_byte_len);
return -1;
}
if(start + int_len * 4 > 128) {
ERROR("efuse_write_otp(type: %s): start + int_len * 4 = %d, it should be <= 128\n",
otp_type_str[type], start + int_len * 4);
return -1;
}
INFO("%s: write %d bytes:\n", otp_type_str[type], wlen);
print_write_read_bytes(0, wbuf, NULL, wlen);
tmpbuf = (unsigned char *)kzalloc(int_len * 4, GFP_KERNEL);
if (tmpbuf == NULL) {
ERROR("efuse_write_otp(type: 0x%s): kzalloc tmpbuf fail\n", otp_type_str[type]);
return -1;
}
// check whether the efuse is programmed
ret = efuse_read_bytes(start, tmpbuf, int_len * 4);
if(ret != int_len * 4) {
ERROR("efuse_write_otp(type: %s): efuse_read_bytes fail\n", otp_type_str[type]);
kfree(tmpbuf);
return -1;
}
for(i = 0; i < int_len * 4; i++) {
if(tmpbuf[i] != 0) {
ERROR("efuse_write_otp(type: %s): eFuse has been programmed. Byte%d = 0x%02X. Exit.\n",
otp_type_str[type], start + i, *(tmpbuf + i));
INFO("Efuse Byte%d ~ Byte%d (%d Bytes) as follows:\n",
start, start + int_len * 4 - 1, int_len * 4);
print_write_read_bytes(0, NULL, tmpbuf, int_len * 4);
kfree(tmpbuf);
return -1;
}
}
// convert bytes to integer
memset(tmpbuf, 0, int_len * 4);
memcpy(tmpbuf, wbuf, wlen);
p_write_int = (unsigned int *)kzalloc(int_len * 4, GFP_KERNEL);
if (p_write_int == NULL) {
ERROR("efuse_write_otp(type: %s): kzalloc p_write_int buffer fail\n", otp_type_str[type]);
kfree(tmpbuf);
return -1;
}
for(i = 0; i < int_len; i++)
p_write_int[i] = tmpbuf[i * 3 + 2] << 16 | (tmpbuf[i * 3 + 1] << 8) | tmpbuf[i * 3];
INFO("write %d raw integers:\n", int_len);
print_write_read_integers(0, p_write_int, NULL, int_len);
// write otp
ret = efuse_hamming_write_int(start, p_write_int, int_len);
if(ret != int_len) {
ERROR("efuse_write_otp(type: %s): efuse_hamming_write_int fail\n", otp_type_str[type]);
kfree(tmpbuf);
kfree(p_write_int);
return -1;
}
kfree(tmpbuf);
kfree(p_write_int);
INFO("efuse_write_otp(type: %s) success\n", otp_type_str[type]);
return wlen;
}
EXPORT_SYMBOL_GPL(efuse_write_otp);
/*------------------------------------------------------------------------------
*
* Function: efuse_read_otp()
* Param:
* type: CPUID,UUID,...,etc
* rbuf: readback bytes
* rlen: how many bytes need to read
* Return:
* return the read bytes number
* If the read bytes number is equal to the excepted bytes number, success.
* Otherwise, fail
*
*------------------------------------------------------------------------------*/
int efuse_read_otp(OTP_TYPE type, unsigned char *rbuf, int rlen)
{
int i, ret, start, byte_len, int_len, max_byte_len;
unsigned int *p_read_int;
unsigned char *p_ecc_error;
INFO("[%s] read %d bytes\n", otp_type_str[type], rlen);
// data length should be 3 aligned
byte_len = bytes_3aligned(rlen);
int_len = byte_len / 3;
switch(type) {
case OTP_BOUND:
max_byte_len = EFUSE_BOUND_MAX_BYTES_NUM;
start = EFUSE_AVAILABLE_START_BYTE;
break;
case OTP_CPUID:
max_byte_len = EFUSE_CPUID_MAX_BYTES_NUM;
start = EFUSE_AVAILABLE_START_BYTE + caculate_need_bytes(EFUSE_BOUND_MAX_BYTES_NUM);
break;
case OTP_UUID:
max_byte_len = EFUSE_UUID_MAX_BYTES_NUM;
start = EFUSE_AVAILABLE_START_BYTE + caculate_need_bytes(EFUSE_BOUND_MAX_BYTES_NUM)
+ caculate_need_bytes(EFUSE_CPUID_MAX_BYTES_NUM);
break;
default:
ERROR("efuse_read_otp: Not support otp type %s\n", otp_type_str[type]);
return -1;
}
if(rlen > max_byte_len) {
ERROR("efuse_read_otp(type: %s): length = %d, it should be <= %d\n",
otp_type_str[type], rlen, max_byte_len);
return -1;
}
if(start + int_len * 4 > 128) {
ERROR("efuse_read_otp(type: %s): start + int_len * 4 = %d, it should be <= 128\n",
otp_type_str[type], start + int_len * 4);
return -1;
}
p_read_int = (unsigned int *)kzalloc(int_len * 4, GFP_KERNEL);
if (p_read_int == NULL) {
ERROR("efuse_read_otp(type: %s): kzalloc p_read_int buffer fail\n", otp_type_str[type]);
return -1;
}
p_ecc_error = (unsigned char *)kzalloc(int_len * 4, GFP_KERNEL);
if(p_ecc_error == NULL) {
ERROR("efuse_read_otp(type: %s): kzalloc p_ecc_error buffer fail\n", otp_type_str[type]);
kfree(p_read_int);
return -1;
}
ret = efuse_hamming_read_int(start, p_read_int, p_ecc_error, int_len);
if(ret != int_len) {
ERROR("efuse_read_otp(type: %s): efuse_hamming_read_int fail\n", otp_type_str[type]);
kfree(p_read_int);
kfree(p_ecc_error);
return -1;
}
INFO("efuse_read_otp: read %d integer\n", int_len);
for(i = 0; i < int_len; i++)
if(p_ecc_error[i] == 0)
INFO("int%3d (Byte%3d ~ Byte%3d): 0x%08X\n",
i, start + i * 4, start + i * 4 + 3, *(p_read_int + i));
else
INFO("int%3d (Byte%3d ~ Byte%3d): 0x%08X (ECC Error Detected)\n",
i, start + i * 4, start + i * 4 + 3, *(p_read_int + i));
for(i = 0; i < rlen; i++)
rbuf[i] = (p_read_int[i / 3] >> ((i % 3) * 8)) & 0xFF;
print_write_read_bytes(1, NULL, rbuf, rlen);
kfree(p_read_int);
kfree(p_ecc_error);
return rlen;
}
EXPORT_SYMBOL_GPL(efuse_read_otp);
static ssize_t register_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
int len = 0;
len += sprintf(buf + len, "VDD25 Level: %d\n", vdd25_control ? gpio_get_value(vdd25_control_pin.gpiono) : -1);
len += sprintf(buf + len, "EFUSE_MODE: 0x%08X\n", EFUSE_MODE_VAL);
len += sprintf(buf + len, "EFUSE_ADR: 0x%08X\n", EFUSE_ADR_VAL);
len += sprintf(buf + len, "EFUSE_DIR_CMD: 0x%08X\n", EFUSE_DIR_CMD_VAL);
len += sprintf(buf + len, "EFUSE_RD_DATA: 0x%08X\n", EFUSE_RD_DATA_VAL);
len += sprintf(buf + len, "EFUSE_ECCSRAM_ADR: 0x%08X\n", EFUSE_ECCSRAM_ADR_VAL);
len += sprintf(buf + len, "EFUSE_ECCSRAM_RDPORT: 0x%08X\n", EFUSE_ECCSRAM_RDPORT_VAL);
len += sprintf(buf + len, "EFUSE_ECC_STATUS_0: 0x%08X\n", EFUSE_ECC_STATUS_0_VAL);
len += sprintf(buf + len, "EFUSE_ECC_STATUS_1: 0x%08X\n", EFUSE_ECC_STATUS_1_VAL);
len += sprintf(buf + len, "\n");
if(vdd25_control) {
if(vdd25_control_pin.gpiono <= WMT_PIN_GP0_GPIO7) {
len += sprintf(buf + len, "GPIO_GP0_INPUT_DATA: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x0000));
len += sprintf(buf + len, "GPIO_GP0_ENABLE: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x0040));
len += sprintf(buf + len, "GPIO_GP0_OUTPUT_ENABLE: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x0080));
len += sprintf(buf + len, "GPIO_GP0_OUTPUT_DATA: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x00C0));
len += sprintf(buf + len, "GPIO_GP0_PULL_ENABLE: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x0480));
len += sprintf(buf + len, "GPIO_GP0_PULL_UP: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x04C0));
} else if(vdd25_control_pin.gpiono <= WMT_PIN_GP1_GPIO15) {
len += sprintf(buf + len, "GPIO_GP1_INPUT_DATA: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x0001));
len += sprintf(buf + len, "GPIO_GP1_ENABLE: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x0041));
len += sprintf(buf + len, "GPIO_GP1_OUTPUT_ENABLE: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x0081));
len += sprintf(buf + len, "GPIO_GP1_OUTPUT_DATA: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x00C1));
len += sprintf(buf + len, "GPIO_GP1_PULL_ENABLE: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x0481));
len += sprintf(buf + len, "GPIO_GP1_PULL_UP: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x04C1));
} else if(vdd25_control_pin.gpiono <= WMT_PIN_GP2_GPIO19) {
len += sprintf(buf + len, "GPIO_GP2_INPUT_DATA: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x0002));
len += sprintf(buf + len, "GPIO_GP2_ENABLE: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x0042));
len += sprintf(buf + len, "GPIO_GP2_OUTPUT_ENABLE: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x0082));
len += sprintf(buf + len, "GPIO_GP2_OUTPUT_DATA: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x00C2));
len += sprintf(buf + len, "GPIO_GP2_PULL_ENABLE: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x0482));
len += sprintf(buf + len, "GPIO_GP2_PULL_UP: 0x%02X\n", REG8_VAL(GPIO_BASE_ADDR + 0x04C2));
}
}
buf[len++] = 0;
return len;
}
static ssize_t raw_data_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
int i, len;
unsigned char read_bytes[128];
efuse_read_bytes(0, read_bytes, 128);
len = 0;
len += sprintf(buf + len, "efuse raw data:");
for(i = 0; i < 128; i++) {
if(i % 16 == 0)
len += sprintf(buf + len, "\n");
if(i != 128 - 1)
len += sprintf(buf + len, "0x%02X,", *(read_bytes + i));
else
len += sprintf(buf + len, "0x%02X\n", *(read_bytes + i));
}
buf[len++] = 0;
return len;
}
static ssize_t available_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
int i;
unsigned char read_bytes[128];
efuse_read_bytes(0, read_bytes, 128);
for(i = 8; i < 128; i++) {
if(read_bytes[i] == 0)
break;
}
if(i != 128)
return sprintf(buf, "Byte %d is available for writing\n", i);
else
return sprintf(buf, "No Byte is available for writing\n");
}
static ssize_t hamming_data_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
int i, len;
unsigned int read_int[30] = {0}; // 120 / 4 = 30
unsigned char ecc_error[30] = {0};
efuse_hamming_read_int(8, read_int, ecc_error, 30);
len = 0;
len += sprintf(buf + len, "\nefuse hamming data:\n");
for(i = 0; i < 30; i++) {
len += sprintf(buf + len, "Byte%3d ~ Byte%3d: 0x%08X",
8 + i * 4, 8 + i * 4 + 3, read_int[i]);
if(ecc_error[i] == 0)
len += sprintf(buf + len, "\n");
else
len += sprintf(buf + len, " ECC Error Detected\n");
}
len += sprintf(buf + len, "\n");
buf[len++] = 0;
return len;
}
static ssize_t cpuid_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
int i, len = 0;
unsigned char read_bytes[EFUSE_CPUID_MAX_BYTES_NUM] = {0};
efuse_read_otp(OTP_CPUID, read_bytes, EFUSE_CPUID_MAX_BYTES_NUM);
for(i = EFUSE_CPUID_MAX_BYTES_NUM - 1; i >= 0; i--)
len += sprintf(buf + len, "%02X", read_bytes[i]);
len += sprintf(buf + len, "\n");
buf[len++] = 0;
return len;
}
#ifdef EFUSE_WRITE_THROUGH_SYSFS
static ssize_t byte_read_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
unsigned long val;
int byte_no;
unsigned char read_byte;
if(count == 0 || *buf < '0' || *buf > '9') {
ERROR("invaild byte_no. buf = %s", buf);
return count;
}
if (strict_strtoul(buf, 0, &val)) {
ERROR("analyze byte_no fail. buf = %s", buf);
return count;
}
byte_no = (int)val;
if(byte_no > 127 || byte_no < 0) {
ERROR("invalid byte_no = %d, byte_no should be 0 ~ 127\n", byte_no);
return count;
}
efuse_read_bytes(byte_no, &read_byte, 1);
INFO("Byte%d = 0x%02X\n", byte_no, read_byte);
return count;
}
static ssize_t byte_write_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
int ret, start_no;
int i, byte_num;
unsigned long value;
unsigned char *p_write_bytes;
const char *p;
char *endp;
if(count == 0 || *buf < '0' || *buf > '9') {
ERROR("invaild start_no. buf = %s", buf);
return count;
}
p = buf;
start_no = simple_strtoul(p, &endp, 0);
if(start_no > 127 || start_no < 8) {
ERROR("invalid start_no = %d, start_no should be 8 ~ 127\n", start_no);
return count;
}
if(*endp == '\0' || *endp == '\n') {
ERROR("wrong input format. format should be \"start_no val1,val2,...\"\n");
return count;
}
p_write_bytes = (unsigned char *)kzalloc(128, GFP_KERNEL);
if(p_write_bytes == NULL) {
ERROR("write_byte_store: kzalloc fail\n");
return count;
}
p = endp + 1;
byte_num = 0;
for(i = 0; i < 128; i++) {
if(*p < '0' || *p > '9') {
ERROR("wrong input format. wrong str1 = \"%s\"\n", p);
kfree(p_write_bytes);
return count;
}
value = simple_strtoul(p, &endp, 0);
if(value > 0xFF) {
ERROR("byte_val = 0x%lx > 0xFF. wrong str = \"%s\"\n", value, p);
kfree(p_write_bytes);
return count;
}
*(p_write_bytes + i) = value & 0xFF;
byte_num++;
if(*endp == '\0' || *endp == '\n')
break;
else if(*endp != ',') {
ERROR("wrong input format. wrong str2 = \"%s\"\n", endp);
kfree(p_write_bytes);
return count;
}
p = endp + 1;
}
if(start_no + byte_num > 128) {
ERROR("start_no = %d, byte_num = %d, start_no + byte_num > 128\n", start_no, byte_num);
kfree(p_write_bytes);
return count;
}
INFO("start_no: %d, byte_num = %d\n", start_no, byte_num);
print_write_read_bytes(1, p_write_bytes, NULL, byte_num);
ret = efuse_write_bytes(start_no, p_write_bytes, byte_num);
if(ret == byte_num)
INFO("eFuse Write Success !!!\n");
kfree(p_write_bytes);
return count;
}
static ssize_t hamming_write_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
int ret, start_no;
const char *p;
char *endp;
unsigned long value;
if(count == 0 || *buf < '0' || *buf > '9') {
ERROR("invaild start_no. buf = %s", buf);
return count;
}
p = buf;
start_no = simple_strtoul(p, &endp, 0);
if(*endp == '\0' || *endp == '\n') {
ERROR("wrong input format. format should be \"start_no value\"\n");
return count;
}
p = endp + 1;
ret = strict_strtoul(p, 0, &value);
if(ret) {
ERROR("invalid value: %s\n", p);
return count;
}
efuse_hamming_write_int(start_no, (unsigned int *)&value, 1);
return count;
}
static ssize_t hamming_read_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
unsigned long val;
int byte_no;
unsigned int read_int = 0;
unsigned char ecc_error = 0;
if(count == 0 || *buf < '0' || *buf > '9') {
ERROR("invaild byte_no. buf = %s", buf);
return count;
}
if (strict_strtoul(buf, 0, &val)) {
ERROR("analyze byte_no fail. buf = %s", buf);
return count;
}
byte_no = (int)val;
if(byte_no > 127 || byte_no < 8) {
ERROR("invalid byte_no = %d, byte_no should be 8 ~ 127\n", byte_no);
return -1;
}
if(byte_no % 4 != 0) {
ERROR("invalid byte_no = %d, byte_no should be 4 aligned\n", byte_no);
return -1;
}
efuse_hamming_read_int(byte_no, &read_int, &ecc_error, 1);
INFO("Hamming read: Byte%d ~ Byte%d, val: 0x%08X\n",
byte_no, byte_no + 3, read_int);
return count;
}
static ssize_t otp_write_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
int i, byte_num, offset = 0, max_byte_len = EFUSE_CPUID_MAX_BYTES_NUM;
OTP_TYPE otp_type = OTP_CPUID;
const char *p = buf;
char *endp;
unsigned char *p_write_bytes;
unsigned long value;
if(strnicmp(buf, "cpuid", 5) == 0) {
otp_type = OTP_CPUID;
max_byte_len = EFUSE_CPUID_MAX_BYTES_NUM;
offset = 5;
} else if(strnicmp(buf, "uuid", 4) == 0) {
otp_type = OTP_UUID;
max_byte_len = EFUSE_UUID_MAX_BYTES_NUM;
offset = 4;
} else {
ERROR("wrong descriptor. format should be \"cpuid(uuid) val1,val2,...\"\n");
return count;
}
p = p + offset;
if(*p == '\0' || *p == '\n') {
ERROR("wrong input format. format should be \"cpuid(uuid) val1,val2,...\"\n");
return count;
}
p_write_bytes = (unsigned char *)kzalloc(128, GFP_KERNEL);
if(p_write_bytes == NULL) {
ERROR("write_otp_store: kzalloc fail\n");
return count;
}
p++;
byte_num = 0;
for(i = 0; i < 128; i++) {
if(*p < '0' || *p > '9') {
ERROR("wrong input format. wrong str1 = \"%s\"\n", p);
kfree(p_write_bytes);
return count;
}
value = simple_strtoul(p, &endp, 0);
if(value > 0xFF) {
ERROR("byte_val = 0x%lx > 0xFF. wrong str = \"%s\"\n", value, p);
kfree(p_write_bytes);
return count;
}
*(p_write_bytes + i) = value & 0xFF;
byte_num++;
if(*endp == '\0' || *endp == '\n')
break;
else if(*endp != ',') {
ERROR("wrong input format. wrong str2 = \"%s\"\n", endp);
kfree(p_write_bytes);
return count;
}
p = endp + 1;
}
if(byte_num > max_byte_len) {
ERROR("byte_num = %d, %s byte_num can't larger than %d\n",
byte_num, otp_type_str[otp_type], max_byte_len);
kfree(p_write_bytes);
return count;
}
efuse_write_otp(otp_type, p_write_bytes, byte_num);
kfree(p_write_bytes);
return count;
}
static ssize_t otp_read_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
int ret, byte_num, offset = 0, max_byte_len = EFUSE_CPUID_MAX_BYTES_NUM;
OTP_TYPE otp_type = OTP_CPUID;
const char *p = buf;
unsigned char *p_read_bytes;
unsigned long value;
if(strnicmp(buf, "bound", 5) == 0) {
otp_type = OTP_BOUND;
max_byte_len = EFUSE_BOUND_MAX_BYTES_NUM;
offset = 5;
}else if(strnicmp(buf, "cpuid", 5) == 0) {
otp_type = OTP_CPUID;
max_byte_len = EFUSE_CPUID_MAX_BYTES_NUM;
offset = 5;
} else if(strnicmp(buf, "uuid", 4) == 0) {
otp_type = OTP_UUID;
max_byte_len = EFUSE_UUID_MAX_BYTES_NUM;
offset = 4;
} else {
ERROR("wrong descriptor. format should be \"cpuid(uuid) bytenum\"\n");
return count;
}
p = p + offset;
if(*p == '\0' || *p == '\n') {
ERROR("wrong input format. format should be \"cpuid(uuid) bytenum\"\n");
return count;
}
p++;
ret = strict_strtoul(p, 0, &value);
if(ret) {
ERROR("invalid value: %s\n", p);
return count;
}
byte_num = (int)value;
if(byte_num <= 0) {
ERROR("byte_num = %d, %s byte_num should be larger than 0\n",
byte_num, otp_type_str[otp_type]);
return count;
}
if(byte_num > max_byte_len) {
ERROR("byte_num = %d, %s byte_num can't larger than %d\n",
byte_num,otp_type_str[otp_type], max_byte_len);
return count;
}
p_read_bytes = (unsigned char *)kzalloc(byte_num, GFP_KERNEL);
if(p_read_bytes == NULL) {
ERROR("read_otp_store: kzalloc fail\n");
return count;
}
efuse_read_otp(otp_type, p_read_bytes, byte_num);
kfree(p_read_bytes);
return count;
}
#endif
static struct class_attribute efuse_class_attrs[] = {
#ifdef EFUSE_WRITE_THROUGH_SYSFS
__ATTR(byte_read, 0200, NULL, byte_read_store),
__ATTR(byte_write, 0200, NULL, byte_write_store),
__ATTR(hamming_read, 0200, NULL, hamming_read_store),
__ATTR(hamming_write, 0200, NULL, hamming_write_store),
__ATTR(otp_read, 0200, NULL, otp_read_store),
__ATTR(otp_write, 0200, NULL, otp_write_store),
#endif
__ATTR_RO(register),
__ATTR_RO(raw_data),
__ATTR_RO(available),
__ATTR_RO(hamming_data),
__ATTR_RO(cpuid),
__ATTR_NULL
};
static struct class efuse_class = {
.name = EFUSE_NAME,
.owner = THIS_MODULE,
.class_attrs = efuse_class_attrs,
};
static int efuse_open(struct inode *inode, struct file *file)
{
DBG("efuse_open\n");
return 0;
}
static int efuse_release(struct inode *inode, struct file *file)
{
DBG("efuse_release\n");
return 0;
}
static ssize_t efuse_otp_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
int ret;
unsigned char *pdata;
unsigned char type;
OTP_TYPE otp_type;
DBG("efuse_otp_read\n");
if(count < 1) {
ERROR("efuse_otp_read: count < 1\n");
return -EINVAL;
}
ret = copy_from_user(&type, buf, 1);
if(ret) {
ERROR("efuse_otp_read: copy_from_user fail\n");
return -EFAULT;
}
if (type < OTP_BOUND || type > OTP_UUID) {
ERROR("efuse_otp_read: invalid otp type: %d\n", type);
return -EINVAL;
}
otp_type = (OTP_TYPE)type;
pdata = kzalloc(count, GFP_KERNEL);
if(!pdata) {
ERROR("efuse_otp_read: alloc memory fail\n");
return -ENOMEM;
}
ret = efuse_read_otp(otp_type, pdata, count);
if(ret != count) {
kfree(pdata);
return -EIO;
}
ret = copy_to_user(buf, pdata, count);
if(ret) {
ERROR("efuse_otp_read: copy_to_user fail\n");
kfree(pdata);
return -EFAULT;
}
kfree(pdata);
return count;
}
static ssize_t efuse_otp_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int ret;
unsigned char *pdata;
unsigned char type;
OTP_TYPE otp_type;
DBG("efuse_otp_write\n");
if(count < 2) {
ERROR("efuse_otp_write: count < 2\n");
return -EINVAL;
}
pdata = kzalloc(count, GFP_KERNEL);
if(!pdata) {
ERROR("efuse_otp_write: alloc memory fail\n");
return -ENOMEM;
}
ret = copy_from_user(pdata, buf, count);
if(ret) {
ERROR("efuse_otp_write: copy_from_user fail\n");
kfree(pdata);
return -EFAULT;
}
type = *pdata;
if (type < OTP_BOUND || type > OTP_UUID) {
ERROR("efuse_otp_write: invalid otp type: %d\n", type);
return -EINVAL;
}
otp_type = (OTP_TYPE)type;
ret = efuse_write_otp(otp_type, pdata + 1, count - 1);
if(ret != (count - 1)) {
kfree(pdata);
return -EIO;
}
kfree(pdata);
return count;
}
static long efuse_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
DBG("efuse_unlocked_ioctl\n");
return 0;
}
static const struct file_operations efuse_fops = {
.owner = THIS_MODULE,
.open = efuse_open,
.release = efuse_release,
.read = efuse_otp_read,
.write = efuse_otp_write,
.unlocked_ioctl = efuse_unlocked_ioctl,
};
static int wmt_efuse_probe(struct platform_device *pdev)
{
int ret;
dev_t dev_no;
unsigned char buf[40];
int num, buflen = 40;
VDD25_GPIO vdd25_gpio;
INFO("wmt_efuse_probe\n");
if(wmt_getsyspara(ENV_EFUSE_GPIO, buf, &buflen) == 0) {
num = sscanf(buf, "%d:%d", &vdd25_gpio.gpiono, &vdd25_gpio.active);
if(num == 2) {
if(vdd25_gpio.gpiono <= WMT_PIN_GP63_SD2CD) {
vdd25_control_pin.gpiono = vdd25_gpio.gpiono;
vdd25_control_pin.active = vdd25_gpio.active;
vdd25_control = 1;
} else
WARNING("wrong %s = %s. gpio_no = %d. It can't larger than %d\n",
ENV_EFUSE_GPIO, buf, vdd25_gpio.gpiono, WMT_PIN_GP63_SD2CD);
} else
WARNING("wrong %s = %s. The param's num = %d. It should be equal to 2\n",
ENV_EFUSE_GPIO, buf, num);
}
if(vdd25_control) {
ret = gpio_request(vdd25_control_pin.gpiono, "efuse-vdd");
if(ret) {
ERROR("gpio(%d) request fail for efuse-vdd\n", vdd25_control_pin.gpiono);
return ret;
}
efuse_vdd25_active(0);
if(vdd25_control_pin.active)
wmt_gpio_setpull(vdd25_control_pin.gpiono, WMT_GPIO_PULL_DOWN);
else
wmt_gpio_setpull(vdd25_control_pin.gpiono, WMT_GPIO_PULL_UP);
}
/*
* create control files in sysfs
* /sys/class/efuse/
*/
ret = class_register(&efuse_class);
if(ret) {
ERROR("register efuse_class fail\n");
if(vdd25_control)
gpio_free(vdd25_control_pin.gpiono);
return -EFAULT;
}
efuse_major = register_chrdev(0, EFUSE_NAME, &efuse_fops);
if(efuse_major < 0) {
ERROR("get efuse_major fail\n");
class_unregister(&efuse_class);
if(vdd25_control)
gpio_free(vdd25_control_pin.gpiono);
return -EFAULT;
}
INFO("mknod /dev/%s c %d 0\n", EFUSE_NAME, efuse_major);
dev_no = MKDEV(efuse_major, 0);
p_efuse_device = device_create(&efuse_class, NULL, dev_no, NULL, EFUSE_NAME);
if (IS_ERR(p_efuse_device)) {
ERROR("create efuse device fail");
unregister_chrdev(efuse_major, EFUSE_NAME);
class_unregister(&efuse_class);
if(vdd25_control)
gpio_free(vdd25_control_pin.gpiono);
return PTR_ERR(p_efuse_device);
}
/* Disable Hardware ECC */
//EFUSE_MODE_VAL &= ~EFUSE_ECC_EN;
return 0;
}
static int wmt_efuse_remove(struct platform_device *pdev)
{
dev_t dev_no;
INFO("wmt_efuse_remove\n");
dev_no = MKDEV(efuse_major, 0);
device_destroy(&efuse_class, dev_no);
unregister_chrdev(efuse_major, EFUSE_NAME);
class_unregister(&efuse_class);
if(vdd25_control)
gpio_free(vdd25_control_pin.gpiono);
return 0;
}
static int wmt_efuse_suspend(struct platform_device *pdev,
pm_message_t state)
{
return 0;
}
static int wmt_efuse_resume(struct platform_device *pdev)
{
INFO("wmt_efuse_resume\n");
if(vdd25_control) {
gpio_re_enabled(vdd25_control_pin.gpiono);
efuse_vdd25_active(0);
if(vdd25_control_pin.active)
wmt_gpio_setpull(vdd25_control_pin.gpiono, WMT_GPIO_PULL_DOWN);
else
wmt_gpio_setpull(vdd25_control_pin.gpiono, WMT_GPIO_PULL_UP);
}
return 0;
}
static struct platform_driver wmt_efuse_driver = {
.probe = wmt_efuse_probe,
.remove = wmt_efuse_remove,
.suspend = wmt_efuse_suspend,
.resume = wmt_efuse_resume,
.driver = {
.name = EFUSE_NAME,
.owner = THIS_MODULE,
},
};
static void wmt_efuse_release(
struct device *device
)
{
INFO("wmt_efuse_release\n");
return;
}
static struct platform_device wmt_efuse_device = {
.name = EFUSE_NAME,
.id = 0,
.dev = {
.release = wmt_efuse_release,
},
.num_resources = 0,
.resource = NULL,
};
static int __init wmt_efuse_init(void)
{
int ret;
INFO("wmt_efuse_init\n");
ret = platform_device_register(&wmt_efuse_device);
if(ret) {
ERROR("can't register eFuse device\n");
return -ENODEV;
}
ret = platform_driver_register(&wmt_efuse_driver);
if(ret) {
ERROR("can't register eFuse driver\n");
platform_device_unregister(&wmt_efuse_device);
return -ENODEV;
}
return 0;
}
static void __exit wmt_efuse_exit(void)
{
INFO("wmt_efuse_exit\n");
platform_driver_unregister(&wmt_efuse_driver);
platform_device_unregister(&wmt_efuse_device);
return;
}
module_init(wmt_efuse_init);
module_exit(wmt_efuse_exit);
MODULE_DESCRIPTION("WM8880 eFuse driver");
MODULE_AUTHOR("WMT ShenZhen Driver Team");
MODULE_LICENSE("GPL");