| /* |
| * drivers/amlogic/efuse/efuse_hw.c |
| * |
| * Copyright (C) 2017 Amlogic, Inc. All rights reserved. |
| * |
| * 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. |
| * |
| */ |
| |
| #include <linux/cdev.h> |
| #include <linux/types.h> |
| #include <linux/fs.h> |
| #include <linux/device.h> |
| #include <linux/slab.h> |
| #include <linux/delay.h> |
| #include <linux/uaccess.h> |
| #include <linux/sched.h> |
| #include <linux/platform_device.h> |
| #include <linux/amlogic/iomap.h> |
| #include <linux/clk.h> |
| #include <linux/clk-provider.h> |
| #ifndef CONFIG_ARM64 |
| #include <asm/opcodes-sec.h> |
| #endif |
| #include <linux/amlogic/meson-secure.h> |
| #include <linux/amlogic/efuse.h> |
| #include "efuse_regs.h" |
| #include "efuse.h" |
| |
| |
| int efuse_active_version = -1; |
| |
| #ifdef EFUSE_DEBUG |
| |
| static unsigned long efuse_test_buf_32[EFUSE_DWORDS] = {0}; |
| static unsigned char *efuse_test_buf_8 = (unsigned char *)efuse_test_buf_32; |
| |
| static void __efuse_write_byte_debug(unsigned long addr, unsigned char data) |
| { |
| efuse_test_buf_8[addr] = data; |
| } |
| |
| static void __efuse_read_dword_debug(unsigned long addr, unsigned long *data) |
| { |
| *data = efuse_test_buf_32[addr >> 2]; |
| } |
| #endif |
| |
| #ifndef EFUSE_DEBUG |
| static void __efuse_write_byte(unsigned long addr, unsigned long data) |
| { |
| unsigned long auto_wr_is_enabled = 0; |
| /* cpu after M8 */ |
| unsigned int byte_sel; |
| |
| clk_prepare_enable(efuse_clk); |
| //set efuse PD=0 |
| aml_set_reg32_bits(P_EFUSE_CNTL1, 0, 27, 1); |
| |
| if (readl((void *) P_EFUSE_CNTL1) & (1 << CNTL1_AUTO_WR_ENABLE_BIT)) { |
| auto_wr_is_enabled = 1; |
| } else { |
| /* temporarily enable Write mode */ |
| aml_set_reg32_bits(P_EFUSE_CNTL1, CNTL1_AUTO_WR_ENABLE_ON, |
| CNTL1_AUTO_WR_ENABLE_BIT, CNTL1_AUTO_WR_ENABLE_SIZE); |
| } |
| |
| /* cpu after M8 */ |
| byte_sel = addr % 4; |
| addr = addr / 4; |
| |
| /* write the address */ |
| aml_set_reg32_bits(P_EFUSE_CNTL1, addr, |
| CNTL1_BYTE_ADDR_BIT, CNTL1_BYTE_ADDR_SIZE); |
| |
| //auto write byte select (0-3), for m8 |
| aml_set_reg32_bits(P_EFUSE_CNTL3, byte_sel, |
| CNTL1_AUTO_WR_START_BIT, 2); |
| |
| /* set starting byte address */ |
| aml_set_reg32_bits(P_EFUSE_CNTL1, CNTL1_BYTE_ADDR_SET_ON, |
| CNTL1_BYTE_ADDR_SET_BIT, CNTL1_BYTE_ADDR_SET_SIZE); |
| aml_set_reg32_bits(P_EFUSE_CNTL1, CNTL1_BYTE_ADDR_SET_OFF, |
| CNTL1_BYTE_ADDR_SET_BIT, CNTL1_BYTE_ADDR_SET_SIZE); |
| |
| /* write the byte */ |
| aml_set_reg32_bits(P_EFUSE_CNTL1, data, |
| CNTL1_BYTE_WR_DATA_BIT, CNTL1_BYTE_WR_DATA_SIZE); |
| /* start the write process */ |
| aml_set_reg32_bits(P_EFUSE_CNTL1, CNTL1_AUTO_WR_START_ON, |
| CNTL1_AUTO_WR_START_BIT, CNTL1_AUTO_WR_START_SIZE); |
| aml_set_reg32_bits(P_EFUSE_CNTL1, CNTL1_AUTO_WR_START_OFF, |
| CNTL1_AUTO_WR_START_BIT, CNTL1_AUTO_WR_START_SIZE); |
| /* dummy read */ |
| readl((void *) P_EFUSE_CNTL1); |
| |
| while (readl((void *)P_EFUSE_CNTL1) & (1 << CNTL1_AUTO_WR_BUSY_BIT)) |
| udelay(1); |
| |
| /* if auto write wasn't enabled and we enabled it, |
| * then disable it upon exit |
| */ |
| if (auto_wr_is_enabled == 0) { |
| aml_set_reg32_bits(P_EFUSE_CNTL1, |
| CNTL1_AUTO_WR_ENABLE_OFF, |
| CNTL1_AUTO_WR_ENABLE_BIT, |
| CNTL1_AUTO_WR_ENABLE_SIZE); |
| } |
| |
| //set efuse PD=1 |
| aml_set_reg32_bits(P_EFUSE_CNTL1, 1, 27, 1); |
| clk_disable_unprepare(efuse_clk); |
| |
| pr_debug("__efuse_write_byte: addr=0x%lx, data=0x%lx\n", addr, data); |
| } |
| |
| static void __efuse_read_dword(unsigned long addr, unsigned long *data) |
| { |
| //unsigned long auto_rd_is_enabled = 0; |
| |
| |
| //if( aml_read_reg32(EFUSE_CNTL1) & ( 1 << CNTL1_AUTO_RD_ENABLE_BIT ) ){ |
| // auto_rd_is_enabled = 1; |
| //} else { |
| /* temporarily enable Read mode */ |
| //aml_set_reg32_bits( P_EFUSE_CNTL1, CNTL1_AUTO_RD_ENABLE_ON, |
| // CNTL1_AUTO_RD_ENABLE_BIT, CNTL1_AUTO_RD_ENABLE_SIZE ); |
| //} |
| |
| //set efuse PD=0 |
| aml_set_reg32_bits(P_EFUSE_CNTL1, 0, 27, 1); |
| |
| /* cpu after M8 */ |
| addr = addr / 4; //each address have 4 bytes in m8 |
| |
| /* write the address */ |
| aml_set_reg32_bits(P_EFUSE_CNTL1, addr, |
| CNTL1_BYTE_ADDR_BIT, CNTL1_BYTE_ADDR_SIZE); |
| /* set starting byte address */ |
| aml_set_reg32_bits(P_EFUSE_CNTL1, CNTL1_BYTE_ADDR_SET_ON, |
| CNTL1_BYTE_ADDR_SET_BIT, CNTL1_BYTE_ADDR_SET_SIZE); |
| aml_set_reg32_bits(P_EFUSE_CNTL1, CNTL1_BYTE_ADDR_SET_OFF, |
| CNTL1_BYTE_ADDR_SET_BIT, CNTL1_BYTE_ADDR_SET_SIZE); |
| |
| /* start the read process */ |
| aml_set_reg32_bits(P_EFUSE_CNTL1, CNTL1_AUTO_WR_START_ON, |
| CNTL1_AUTO_RD_START_BIT, CNTL1_AUTO_RD_START_SIZE); |
| aml_set_reg32_bits(P_EFUSE_CNTL1, CNTL1_AUTO_WR_START_OFF, |
| CNTL1_AUTO_RD_START_BIT, CNTL1_AUTO_RD_START_SIZE); |
| /* dummy read */ |
| readl((void *)P_EFUSE_CNTL1); |
| |
| while (readl((void *)P_EFUSE_CNTL1) & (1 << CNTL1_AUTO_RD_BUSY_BIT)) |
| udelay(1); |
| |
| /* read the 32-bits value */ |
| (*data) = readl((void *)P_EFUSE_CNTL2); |
| |
| //set efuse PD=1 |
| aml_set_reg32_bits(P_EFUSE_CNTL1, 1, 27, 1); |
| /* if auto read wasn't enabled and we enabled it, |
| * then disable it upon exit |
| */ |
| //if ( auto_rd_is_enabled == 0 ){ |
| //aml_set_reg32_bits( P_EFUSE_CNTL1, CNTL1_AUTO_RD_ENABLE_OFF, |
| // CNTL1_AUTO_RD_ENABLE_BIT, CNTL1_AUTO_RD_ENABLE_SIZE ); |
| //} |
| |
| pr_debug("__efuse_read_dword: addr=%ld, data=0x%lx\n", addr, *data); |
| } |
| #endif |
| |
| static ssize_t __efuse_read(char *buf, size_t count, loff_t *ppos) |
| { |
| unsigned long *contents = kzalloc(sizeof(unsigned long)*EFUSE_DWORDS, |
| GFP_KERNEL); |
| unsigned int pos = *ppos; |
| unsigned long *pdw; |
| char *tmp_p; |
| /*pos may not align to 4*/ |
| unsigned int dwsize = (count + 3 + pos%4) >> 2; |
| struct efuse_hal_api_arg arg; |
| unsigned long retcnt; |
| int ret; |
| |
| if (!contents) { |
| pr_info("memory not enough\n"); |
| return -ENOMEM; |
| } |
| |
| if (pos >= EFUSE_BYTES) |
| return 0; |
| |
| if (count > EFUSE_BYTES - pos) |
| count = EFUSE_BYTES - pos; |
| if (count > EFUSE_BYTES) |
| return -EFAULT; |
| |
| if (!meson_secure_enabled()) { |
| clk_prepare_enable(efuse_clk); |
| aml_set_reg32_bits(P_EFUSE_CNTL1, CNTL1_AUTO_RD_ENABLE_ON, |
| CNTL1_AUTO_RD_ENABLE_BIT, CNTL1_AUTO_RD_ENABLE_SIZE); |
| |
| for (pdw = contents + pos/4; |
| dwsize-- > 0 && pos < EFUSE_BYTES; |
| pos += 4, ++pdw) { |
| #ifdef EFUSE_DEBUG |
| __efuse_read_dword_debug(pos, pdw); |
| #else |
| /* if pos does not align to 4, __efuse_read_dword |
| * read from next dword, so, discount this un-aligned |
| * partition |
| */ |
| __efuse_read_dword((pos - pos%4), pdw); |
| #endif |
| } |
| |
| aml_set_reg32_bits(P_EFUSE_CNTL1, |
| CNTL1_AUTO_RD_ENABLE_OFF, |
| CNTL1_AUTO_RD_ENABLE_BIT, |
| CNTL1_AUTO_RD_ENABLE_SIZE); |
| |
| clk_disable_unprepare(efuse_clk); |
| tmp_p = (char *)contents; |
| tmp_p += *ppos; |
| |
| memcpy(buf, tmp_p, count); |
| |
| *ppos += count; |
| } else { |
| arg.cmd = EFUSE_HAL_API_READ; |
| arg.offset = pos; |
| arg.size = count; |
| arg.buffer = virt_to_phys(contents); |
| arg.retcnt = virt_to_phys(&retcnt); |
| ret = meson_trustzone_efuse((void *)&arg); |
| if (ret == 0) { |
| count = retcnt; |
| *ppos += retcnt; |
| memcpy(buf, contents, retcnt); |
| } else |
| count = 0; |
| } |
| |
| /*if (contents)*/ |
| kfree(contents); |
| |
| return count; |
| } |
| |
| static ssize_t __efuse_write(const char *buf, size_t count, loff_t *ppos) |
| { |
| unsigned int pos = *ppos; |
| unsigned char *pc; |
| struct efuse_hal_api_arg arg; |
| unsigned int retcnt; |
| int ret; |
| |
| if (pos >= EFUSE_BYTES) |
| return 0; /* Past EOF */ |
| if (count > EFUSE_BYTES - pos) |
| count = EFUSE_BYTES - pos; |
| if (count > EFUSE_BYTES) |
| return -EFAULT; |
| |
| if (!meson_secure_enabled()) { |
| for (pc = (char *)buf; count--; ++pos, ++pc) |
| #ifdef EFUSE_DEBUG |
| __efuse_write_byte_debug(pos, *pc); |
| #else |
| __efuse_write_byte(pos, *pc); |
| #endif |
| |
| *ppos = pos; |
| ret = (const char *)pc - buf; |
| } else { |
| arg.cmd = EFUSE_HAL_API_WRITE; |
| arg.offset = pos; |
| arg.size = count; |
| arg.buffer = virt_to_phys(buf); |
| arg.retcnt = virt_to_phys(&retcnt); |
| ret = meson_trustzone_efuse((void *)&arg); |
| if (ret == 0) { |
| *ppos = pos+retcnt; |
| ret = retcnt; |
| } else |
| ret = 0; |
| } |
| |
| return ret; |
| } |
| |
| ssize_t aml__efuse_read(char *buf, size_t count, loff_t *ppos) |
| { |
| return __efuse_read(buf, count, ppos); |
| } |
| ssize_t aml__efuse_write(const char *buf, size_t count, loff_t *ppos) |
| { |
| return __efuse_write(buf, count, ppos); |
| } |
| |
| /* ================================================ */ |
| |
| /* #define SOC_CHIP_TYPE_TEST */ |
| #ifdef SOC_CHIP_TYPE_TEST |
| static char *soc_chip[] = { |
| {"efuse soc chip m8baby"}, |
| {"efuse soc chip unknown"}, |
| }; |
| #endif |
| |
| struct efuse_chip_identify_t { |
| unsigned int chiphw_mver; |
| unsigned int chiphw_subver; |
| unsigned int chiphw_thirdver; |
| enum efuse_socchip_type_e type; |
| }; |
| static const struct efuse_chip_identify_t efuse_chip_hw_info[] = { |
| { |
| .chiphw_mver = 27, |
| .chiphw_subver = 0, |
| .chiphw_thirdver = 0, |
| .type = EFUSE_SOC_CHIP_M8BABY |
| }, |
| }; |
| #define EFUSE_CHIP_HW_INFO_NUM (sizeof(efuse_chip_hw_info)/ \ |
| sizeof(efuse_chip_hw_info[0])) |
| |
| |
| enum efuse_socchip_type_e efuse_get_socchip_type(void) |
| { |
| enum efuse_socchip_type_e type; |
| unsigned int regval; |
| int i; |
| struct efuse_chip_identify_t *pinfo = |
| (struct efuse_chip_identify_t *)&efuse_chip_hw_info[0]; |
| type = EFUSE_SOC_CHIP_UNKNOWN; |
| regval = aml_read_cbus(ASSIST_HW_REV); |
| /* pr_info("chip ASSIST_HW_REV reg:%d\n",regval); */ |
| for (i = 0; i < EFUSE_CHIP_HW_INFO_NUM; i++) { |
| if (pinfo->chiphw_mver == regval) { |
| type = pinfo->type; |
| break; |
| } |
| pinfo++; |
| } |
| |
| #ifdef SOC_CHIP_TYPE_TEST |
| pr_info("%s\n", soc_chip[type]); |
| #endif |
| return type; |
| } |
| |
| static int efuse_checkversion(char *buf) |
| { |
| enum efuse_socchip_type_e soc_type; |
| int i; |
| int ver = buf[0]; |
| |
| for (i = 0; i < efuseinfo_num; i++) { |
| if (efuseinfo[i].version == ver) { |
| soc_type = efuse_get_socchip_type(); |
| switch (soc_type) { |
| case EFUSE_SOC_CHIP_M8BABY: |
| if (ver != M8_EFUSE_VERSION_SERIALNUM_V1) |
| ver = -1; |
| break; |
| case EFUSE_SOC_CHIP_UNKNOWN: |
| default: |
| pr_info("%s:%d soc is unknown\n", |
| __func__, __LINE__); |
| ver = -1; |
| break; |
| } |
| return ver; |
| } |
| } |
| |
| return -1; |
| } |
| |
| |
| static int efuse_set_versioninfo(struct efuseinfo_item_t *info) |
| { |
| int ret = -1; |
| enum efuse_socchip_type_e soc_type; |
| |
| strcpy(info->title, "version"); |
| info->id = EFUSE_VERSION_ID; |
| soc_type = efuse_get_socchip_type(); |
| switch (soc_type) { |
| case EFUSE_SOC_CHIP_M8BABY: |
| info->offset = M8_EFUSE_VERSION_OFFSET; /* 509 */ |
| info->data_len = M8_EFUSE_VERSION_DATA_LEN; |
| ret = 0; |
| break; |
| case EFUSE_SOC_CHIP_UNKNOWN: |
| default: |
| pr_info("%s:%d chip is unknown, use default M8 chip\n", |
| __func__, __LINE__); |
| info->offset = M8_EFUSE_VERSION_OFFSET; /* 509 */ |
| info->data_len = M8_EFUSE_VERSION_DATA_LEN; |
| ret = 0; |
| break; |
| break; |
| } |
| |
| return ret; |
| } |
| |
| |
| static int efuse_readversion(void) |
| { |
| char ver_buf[4], buf[4]; |
| struct efuseinfo_item_t info; |
| int ret; |
| |
| if (efuse_active_version != -1) |
| return efuse_active_version; |
| |
| ret = efuse_set_versioninfo(&info); |
| if (ret < 0) |
| return ret; |
| |
| memset(ver_buf, 0, sizeof(ver_buf)); |
| memset(buf, 0, sizeof(buf)); |
| __efuse_read(buf, info.data_len, &info.offset); |
| memcpy(ver_buf, buf, sizeof(buf)); |
| ret = efuse_checkversion(ver_buf); /* m3,m6,m8 */ |
| if ((ret > 0) && (ver_buf[0] != 0)) { |
| efuse_active_version = ver_buf[0]; |
| return ver_buf[0]; /* version right */ |
| } else |
| return -1; /* version err */ |
| } |
| |
| static int efuse_getinfo_byPOS(unsigned int pos, struct efuseinfo_item_t *info) |
| { |
| int ver; |
| int i; |
| struct efuseinfo_t *vx = NULL; |
| struct efuseinfo_item_t *item = NULL; |
| int size; |
| int ret = -1; |
| enum efuse_socchip_type_e soc_type; |
| |
| unsigned int versionPOS; |
| |
| soc_type = efuse_get_socchip_type(); |
| switch (soc_type) { |
| case EFUSE_SOC_CHIP_M8BABY: |
| versionPOS = M8_EFUSE_VERSION_OFFSET; /* 509 */ |
| break; |
| |
| case EFUSE_SOC_CHIP_UNKNOWN: |
| default: |
| pr_info("%s:%d chip is unknown\n", __func__, __LINE__); |
| return -1; |
| /* break; */ |
| } |
| |
| if (pos == versionPOS) { |
| ret = efuse_set_versioninfo(info); |
| return ret; |
| } |
| |
| ver = efuse_readversion(); |
| if (ver < 0) { |
| pr_info("efuse version is not selected.\n"); |
| return -1; |
| } |
| |
| for (i = 0; i < efuseinfo_num; i++) { |
| if (efuseinfo[i].version == ver) { |
| vx = &(efuseinfo[i]); |
| break; |
| } |
| } |
| if (!vx) { |
| pr_info("efuse version %d is not supported.\n", ver); |
| return -1; |
| } |
| |
| item = vx->efuseinfo_version; |
| size = vx->size; |
| ret = -1; |
| for (i = 0; i < size; i++, item++) { |
| if (pos == item->offset) { |
| strcpy(info->title, item->title); |
| info->offset = item->offset; |
| info->id = item->id; |
| info->data_len = item->data_len; |
| /* /what's up ? typo error? */ |
| ret = 0; |
| break; |
| } |
| } |
| |
| if (ret < 0) |
| pr_info("POS:%d is not found.\n", pos); |
| |
| return ret; |
| } |
| |
| /* ================================================ */ |
| /* public interface */ |
| /* ================================================ */ |
| |
| int efuse_getinfo_byTitle(unsigned char *title, struct efuseinfo_item_t *info) |
| { |
| int ver; |
| int i; |
| struct efuseinfo_t *vx = NULL; |
| struct efuseinfo_item_t *item = NULL; |
| int size; |
| int ret = -1; |
| |
| if (!strcmp(title, "version")) { |
| ret = efuse_set_versioninfo(info); |
| return ret; |
| } |
| |
| ver = efuse_readversion(); |
| if (ver < 0) { |
| pr_info("efuse version is not selected.\n"); |
| return -1; |
| } |
| |
| for (i = 0; i < efuseinfo_num; i++) { |
| if (efuseinfo[i].version == ver) { |
| vx = &(efuseinfo[i]); |
| break; |
| } |
| } |
| if (!vx) { |
| pr_info("efuse version %d is not supported.\n", ver); |
| return -1; |
| } |
| |
| item = vx->efuseinfo_version; |
| size = vx->size; |
| ret = -1; |
| for (i = 0; i < size; i++, item++) { |
| if (!strcmp(title, item->title)) { |
| info->id = item->id; |
| strcpy(info->title, item->title); |
| info->offset = item->offset; |
| info->id = item->id; |
| info->data_len = item->data_len; |
| ret = 0; |
| break; |
| } |
| } |
| |
| if (ret < 0) |
| pr_info("title: %s is not found.\n", title); |
| |
| return ret; |
| } |
| |
| |
| int check_if_efused(loff_t pos, size_t count) |
| { |
| loff_t local_pos = pos; |
| int i; |
| unsigned char *buf = NULL; |
| struct efuseinfo_item_t info; |
| |
| if (efuse_getinfo_byPOS(pos, &info) < 0) { |
| pr_info("not found the position:%lld.\n", pos); |
| return -1; |
| } |
| if (count > info.data_len) { |
| pr_info("data length: %zd is out of EFUSE layout!\n", count); |
| return -1; |
| } |
| if (count == 0) { |
| pr_info("data length: 0 is error!\n"); |
| return -1; |
| } |
| |
| buf = kcalloc(info.data_len, sizeof(char), GFP_KERNEL); |
| if (buf) { |
| if (__efuse_read(buf, info.data_len, &local_pos) |
| == info.data_len) { |
| for (i = 0; i < info.data_len; i++) { |
| if (buf[i]) { |
| pr_info("pos %zd value is %d", |
| (size_t)(pos + i), buf[i]); |
| return 1; |
| } |
| } |
| } |
| } else { |
| pr_info("no memory\n"); |
| return -ENOMEM; |
| } |
| |
| kfree(buf); |
| buf = NULL; |
| return 0; |
| } |
| |
| int efuse_read_item(char *buf, size_t count, loff_t *ppos) |
| { |
| char *data_buf = NULL; |
| char *pdata = NULL; |
| |
| unsigned int pos = (unsigned int)*ppos; |
| struct efuseinfo_item_t info; |
| |
| if (efuse_getinfo_byPOS(pos, &info) < 0) { |
| pr_info("not found the position:%d.\n", pos); |
| return -1; |
| } |
| |
| if (count > info.data_len) { |
| pr_info("data length: %zd is out of EFUSE layout!\n", count); |
| return -1; |
| } |
| if (count == 0) { |
| pr_info("data length: 0 is error!\n"); |
| return -1; |
| } |
| |
| data_buf = kzalloc(sizeof(char)*EFUSE_BYTES, GFP_KERNEL); |
| if (!data_buf) { |
| /* pr_info("memory not enough\n");*/ |
| return -ENOMEM; |
| } |
| |
| pdata = data_buf; |
| __efuse_read(pdata, info.data_len, ppos); |
| |
| memcpy(buf, data_buf, count); |
| |
| /*if (data_buf)*/ |
| kfree(data_buf); |
| return count; |
| } |
| |
| int efuse_write_item(char *buf, size_t count, loff_t *ppos) |
| { |
| |
| char *data_buf = NULL; |
| char *pdata = NULL; |
| unsigned int data_len; |
| |
| unsigned int pos = (unsigned int)*ppos; |
| struct efuseinfo_item_t info; |
| |
| if (efuse_getinfo_byPOS(pos, &info) < 0) { |
| pr_info("not found the position:%d.\n", pos); |
| return -1; |
| } |
| #ifndef CONFIG_AMLOGIC_EFUSE_WRITE_VERSION_PERMIT |
| if (strcmp(info.title, "version") == 0) { |
| pr_info("prohibit write version in kernel\n"); |
| return 0; |
| } |
| #endif |
| |
| if (count > info.data_len) { |
| pr_info("data length: %zd is out of EFUSE layout!\n", count); |
| return -1; |
| } |
| if (count == 0) { |
| pr_info("data length: 0 is error!\n"); |
| return -1; |
| } |
| |
| data_buf = kzalloc(sizeof(char)*EFUSE_BYTES, GFP_KERNEL); |
| if (!data_buf) { |
| /* pr_info("memory not enough\n");*/ |
| return -ENOMEM; |
| } |
| |
| memcpy(data_buf, buf, count); |
| |
| pdata = data_buf; |
| |
| data_len = info.data_len; |
| |
| __efuse_write(data_buf, data_len, ppos); |
| |
| kfree(data_buf); |
| |
| return data_len; |
| } |
| |
| /* function: efuse_read_intlItem |
| * intl_item: item name,name is [temperature,cvbs_trimming,temper_cvbs] |
| * [temperature: 2byte] |
| * [cvbs_trimming: 2byte] |
| * [temper_cvbs: 4byte] |
| * buf: output para |
| * size: buf size |
| * return: <0 fail, >=0 ok |
| */ |
| int efuse_read_intlItem(char *intl_item, char *buf, int size) |
| { |
| enum efuse_socchip_type_e soc_type; |
| loff_t pos; |
| int len; |
| int ret = -1; |
| |
| soc_type = efuse_get_socchip_type(); |
| switch (soc_type) { |
| case EFUSE_SOC_CHIP_M8BABY: |
| if (strcasecmp(intl_item, "temperature") == 0) { |
| pos = 502; |
| len = 2; |
| if (size <= 0) { |
| pr_err("%s input size:%d is error\n", |
| intl_item, size); |
| return -1; |
| } |
| if (len > size) |
| len = size; |
| |
| ret = __efuse_read(buf, len, &pos); |
| return ret; |
| } |
| if (strcasecmp(intl_item, "cvbs_trimming") == 0) { |
| /* cvbs note: |
| * cvbs has 2 bytes, position is 504 and 505, |
| * 504 is low byte,505 is high byte |
| * p504[bit2~0] is cvbs trimming CDAC_GSW<2:0> |
| * p505[bit7-6] : 10--wrote cvbs, |
| * 00-- not wrote cvbs |
| */ |
| pos = 504; |
| len = 2; |
| if (size <= 0) { |
| pr_err("%s input size:%d is error\n", |
| intl_item, size); |
| return -1; |
| } |
| if (len > size) { |
| len = size; |
| ret = __efuse_read(buf, len, &pos); |
| return ret; |
| } |
| } |
| if (strcasecmp(intl_item, "temper_cvbs") == 0) { |
| pos = 502; |
| len = 4; |
| if (size <= 0) { |
| pr_err("%s input size:%d is error\n", |
| intl_item, size); |
| return -1; |
| } |
| if (len > size) |
| len = size; |
| ret = __efuse_read(buf, len, &pos); |
| return ret; |
| } |
| break; |
| case EFUSE_SOC_CHIP_UNKNOWN: |
| default: |
| pr_err("%s:%d chip is unknown\n", __func__, __LINE__); |
| //return -1; |
| break; |
| } |
| return ret; |
| } |