summaryrefslogtreecommitdiff
path: root/drivers/misc/sprd_otp
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/misc/sprd_otp
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/misc/sprd_otp')
-rw-r--r--drivers/misc/sprd_otp/Kconfig32
-rw-r--r--drivers/misc/sprd_otp/Makefile8
-rw-r--r--drivers/misc/sprd_otp/__regs_ana_efuse.h81
-rw-r--r--drivers/misc/sprd_otp/__regs_efuse.h114
-rw-r--r--drivers/misc/sprd_otp/ana_efuse.c216
-rw-r--r--drivers/misc/sprd_otp/core.c214
-rw-r--r--drivers/misc/sprd_otp/efuse.c352
-rw-r--r--drivers/misc/sprd_otp/laser_fuse.c158
-rw-r--r--drivers/misc/sprd_otp/otp-bitmap.c83
-rw-r--r--drivers/misc/sprd_otp/otp-helper.c449
-rw-r--r--drivers/misc/sprd_otp/sprd_otp.h51
11 files changed, 1758 insertions, 0 deletions
diff --git a/drivers/misc/sprd_otp/Kconfig b/drivers/misc/sprd_otp/Kconfig
new file mode 100644
index 00000000..740bab3b
--- /dev/null
+++ b/drivers/misc/sprd_otp/Kconfig
@@ -0,0 +1,32 @@
+menuconfig OTP_SPRD
+
+ bool "Spreadtrum OTP Efuse"
+ depends on ARCH_SCX30G || ARCH_SCX35L
+ default y
+ help
+ Spreadtrum OTP Efuse Support.
+
+if OTP_SPRD
+
+config OTP_SPRD_EFUSE
+ tristate "Spreadtrum D-die AON Efuse driver (since from r3p0)"
+ depends on ARCH_SCX30G || ARCH_SCX35L
+ default y
+ help
+ Say Y here to support spreadtrum ddie efuse for ip r3p0 version.
+
+config OTP_SPRD_ADIE_EFUSE
+ tristate "Spreadtrum A-die eFuse/eMemory driver"
+ depends on ADIE_SC2723 || ADIE_SC2723S
+ default y
+ help
+ Say Y here to support spreadtrum adie efuse.
+
+config OTP_SPRD_LASER_FUSE
+ tristate "Spreadtrum A-die laser fuse driver (no longer recommended)"
+ depends on ADIE_SC2713 || ADIE_SC2713S
+ default y
+ help
+ Say Y here to support spreadtrum adie laser fuse.
+
+endif
diff --git a/drivers/misc/sprd_otp/Makefile b/drivers/misc/sprd_otp/Makefile
new file mode 100644
index 00000000..17978b9c
--- /dev/null
+++ b/drivers/misc/sprd_otp/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for spredtrum efuse drivers.
+#
+
+obj-$(CONFIG_OTP_SPRD) += core.o otp-helper.o
+obj-$(CONFIG_OTP_SPRD_EFUSE) += efuse.o
+obj-$(CONFIG_OTP_SPRD_ADIE_EFUSE) += ana_efuse.o otp-bitmap.o
+obj-$(CONFIG_OTP_SPRD_LASER_FUSE) += laser_fuse.o
diff --git a/drivers/misc/sprd_otp/__regs_ana_efuse.h b/drivers/misc/sprd_otp/__regs_ana_efuse.h
new file mode 100644
index 00000000..5a4d6cd0
--- /dev/null
+++ b/drivers/misc/sprd_otp/__regs_ana_efuse.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013 Spreadtrum Communications 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.
+ *
+ *************************************************
+ * Automatically generated C header: do not edit *
+ *************************************************
+ */
+
+#ifndef __ANA_REGS_EFUSE_H__
+#define __ANA_REGS_EFUSE_H__
+
+#define ANA_REGS_EFUSE
+
+/* registers definitions for controller ANA_REGS_EFUSE */
+#define ANA_REG_EFUSE_GLB_CTRL SCI_ADDR(ANA_REGS_EFUSE_BASE, 0x0000)
+#define ANA_REG_EFUSE_DATA_RD SCI_ADDR(ANA_REGS_EFUSE_BASE, 0x0004)
+#define ANA_REG_EFUSE_DATA_WR SCI_ADDR(ANA_REGS_EFUSE_BASE, 0x0008)
+#define ANA_REG_EFUSE_BLOCK_INDEX SCI_ADDR(ANA_REGS_EFUSE_BASE, 0x000c)
+#define ANA_REG_EFUSE_MODE_CTRL SCI_ADDR(ANA_REGS_EFUSE_BASE, 0x0010)
+#define ANA_REG_EFUSE_STATUS SCI_ADDR(ANA_REGS_EFUSE_BASE, 0x0014)
+#define ANA_REG_EFUSE_WR_TIMING_CTRL SCI_ADDR(ANA_REGS_EFUSE_BASE, 0x0028)
+#define ANA_REG_EFUSE_RD_TIMING_CTRL SCI_ADDR(ANA_REGS_EFUSE_BASE, 0x002c)
+#define ANA_REG_EFUSE_EFUSE_DEB_CTRL SCI_ADDR(ANA_REGS_EFUSE_BASE, 0x0030)
+
+/* bits definitions for register ANA_REG_EFUSE_GLB_CTRL */
+/* Efuse SW programme enable.
+ */
+#define BIT_EFUSE_PGM_EN ( BIT(0) )
+/* Efuse type select, 00:TSMC, 01, 1x reserved.
+ */
+#define BITS_EFUSE_TYPE(_x_) ( (_x_) << 1 & (BIT(1)|BIT(2)) )
+
+/* bits definitions for register ANA_REG_EFUSE_DATA_RD */
+#define BITS_EFUSE_DATA_RD(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) )
+
+/* bits definitions for register ANA_REG_EFUSE_DATA_WR */
+#define BITS_EFUSE_DATA_WR(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) )
+
+/* bits definitions for register ANA_REG_EFUSE_BLOCK_INDEX */
+#define BITS_READ_WRITE_INDEX(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)) )
+
+#define SHFT_READ_WRITE_INDEX ( 0 )
+#define MASK_READ_WRITE_INDEX ( BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4) )
+
+/* bits definitions for register ANA_REG_EFUSE_MODE_CTRL */
+/* Write 1 to this bit start A_PGM mode(array PGM mode).
+ * This bit is self-clear, read this bit will always get 0.
+ */
+#define BIT_PG_START ( BIT(0) )
+#define BIT_RD_START ( BIT(1) )
+#define BIT_STANDBY_START ( BIT(2) )
+
+/* bits definitions for register ANA_REG_EFUSE_STATUS */
+#define BIT_PGM_BUSY ( BIT(0) )
+#define BIT_READ_BUSY ( BIT(1) )
+#define BIT_STANDBY_BUSY ( BIT(2) )
+
+/* bits definitions for register ANA_REG_EFUSE_WR_TIMING_CTRL */
+#define BITS_EFUSE_WR_TIMING(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)) )
+
+/* bits definitions for register ANA_REG_EFUSE_RD_TIMING_CTRL */
+#define BITS_EFUSE_RD_TIMING(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)) )
+
+/* bits definitions for register ANA_REG_EFUSE_EFUSE_DEB_CTRL */
+#define BIT_MARGIN_MODE_EN ( BIT(1) )
+#define BIT_DOUBLE_BIT_DISABLE ( BIT(0) )
+
+/* vars definitions for controller ANA_REGS_EFUSE */
+#define EFUSE_MAGIC_NUMBER ( 0x2723 )
+
+#endif /* __ANA_REGS_EFUSE_H__ */
diff --git a/drivers/misc/sprd_otp/__regs_efuse.h b/drivers/misc/sprd_otp/__regs_efuse.h
new file mode 100644
index 00000000..d597ef97
--- /dev/null
+++ b/drivers/misc/sprd_otp/__regs_efuse.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications 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.
+ *
+ *************************************************
+ * Automatically generated C header: do not edit *
+ *************************************************
+ */
+
+#ifndef __REGS_EFUSE_H__
+#define __REGS_EFUSE_H__
+
+#define REGS_EFUSE
+
+/* registers definitions for controller REGS_EFUSE */
+#define REG_EFUSE_DATA_RD SCI_ADDR(REGS_EFUSE_BASE, 0x0000)
+#define REG_EFUSE_DATA_WR SCI_ADDR(REGS_EFUSE_BASE, 0x0004)
+#define REG_EFUSE_READ_WRITE_INDEX SCI_ADDR(REGS_EFUSE_BASE, 0x0008)
+#define REG_EFUSE_MODE_CTRL SCI_ADDR(REGS_EFUSE_BASE, 0x000c)
+#define REG_EFUSE_CFG0 SCI_ADDR(REGS_EFUSE_BASE, 0x0010)
+#define REG_EFUSE_CFG1 SCI_ADDR(REGS_EFUSE_BASE, 0x0014)
+#define REG_EFUSE_STATUS SCI_ADDR(REGS_EFUSE_BASE, 0x0020)
+#define REG_EFUSE_BLK_FLAG0 SCI_ADDR(REGS_EFUSE_BASE, 0x0024)
+#define REG_EFUSE_BLK_FLAG1 SCI_ADDR(REGS_EFUSE_BASE, 0x0028)
+#define REG_EFUSE_BLK_FLAG0_CLR SCI_ADDR(REGS_EFUSE_BASE, 0x0030)
+#define REG_EFUSE_BLK_FLAG1_CLR SCI_ADDR(REGS_EFUSE_BASE, 0x0034)
+#define REG_EFUSE_MAGIC_NUMBER SCI_ADDR(REGS_EFUSE_BASE, 0x0040)
+#define REG_EFUSE_STROBE_LOW_WIDTH SCI_ADDR(REGS_EFUSE_BASE, 0x0044)
+#define REG_EFUSE_EFUSE_DEB_CTRL SCI_ADDR(REGS_EFUSE_BASE, 0x0048)
+
+/* bits definitions for register REG_EFUSE_READ_WRITE_INDEX */
+#define BITS_READ_WRITE_INDEX(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)) )
+
+#define SHFT_READ_WRITE_INDEX ( 0 )
+#define MASK_READ_WRITE_INDEX ( BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4) )
+
+/* bits definitions for register REG_EFUSE_MODE_CTRL */
+/* Write 1 to this bit start A_PGM mode(array PGM mode).
+ * This bit is self-clear, read this bit will always get 0.
+ */
+#define BIT_PG_START ( BIT(0) )
+#define BIT_RD_START ( BIT(1) )
+#define BIT_STANDBY_START ( BIT(2) )
+
+/* bits definitions for register REG_EFUSE_CFG0 */
+#define BITS_TPGM_TIME_CNT(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)) )
+#define BITS_EFS_TYPE(_x_) ( (_x_) << 16 & (BIT(16)|BIT(17)) )
+#define BIT_EFS_VDDQ_K1_ON ( BIT(28) )
+#define BIT_EFS_VDDQ_K2_ON ( BIT(29) )
+/* Set this bit will open 0.9v static power supply for efuse memory,
+ * before any operation towards to efuse memory this bit have to set to 1.
+ * Once this bit is cleared, the efuse will go to power down mode.
+ */
+#define BIT_EFS_VDD_ON ( BIT(30) )
+/* ONly set this bit can SW write register field of TPGM_TIME_CNT and start PGM mode,
+ * for protect sw unexpectedly programmed efuse memory.
+ */
+#define BIT_PGM_EN ( BIT(31) )
+
+/* bits definitions for register REG_EFUSE_CFG1 */
+#define BIT_BLK0_AUTO_TEST_EN ( BIT(0) )
+
+/* bits definitions for register REG_EFUSE_STATUS */
+#define BIT_PGM_BUSY ( BIT(0) )
+#define BIT_READ_BUSY ( BIT(1) )
+#define BIT_STANDBY_BUSY ( BIT(2) )
+
+/* bits definitions for register REG_EFUSE_BLK_FLAG0 */
+#define BIT_BLK0_PROT_FLAG ( BIT(0) )
+
+/* bits definitions for register REG_EFUSE_BLK_FLAG1 */
+#define BIT_BLK0_ERR_FLAG ( BIT(0) )
+
+/* bits definitions for register REG_EFUSE_BLK_FLAG0_CLR */
+#define BIT_BLK0_PROT_FLAG_CLR ( BIT(0) )
+
+/* bits definitions for register REG_EFUSE_BLK_FLAG1_CLR */
+#define BIT_BLK0_ERR_FLAG_CLR ( BIT(0) )
+
+/* bits definitions for register REG_EFUSE_MAGIC_NUMBER */
+/* Magic number, only when this field is 0x8810, the efuse programming command can be handle.
+ * So, if SW want to program efuse memory, except open clocks and power, the follow conditions must be met:
+ * 1. PGM_EN = 1;
+ * 2. EFUSE_MAGIC_NUMBER = 0x8810
+ */
+#define BITS_MAGIC_NUMBER(_x_) ( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) )
+#define BITS_DEB_MAGIC_NUMBER(_x_) ( (_x_) << 16 & (BIT(16)|BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)|BIT(24)|BIT(25)|BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) )
+
+/* bits definitions for register REG_EFUSE_STROBE_LOW_WIDTH */
+#define BITS_EFUSE_STROBE_LOW_WIDTH(_x_)( (_x_) << 0 & (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) )
+#define BITS_CLK_EFS_DIV(_x_) ( (_x_) << 16 & (BIT(16)|BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)) )
+
+#define SHFT_CLK_EFS_DIV ( 16 )
+#define MASK_CLK_EFS_DIV ( BIT(16)|BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23) )
+
+/* bits definitions for register REG_EFUSE_EFUSE_DEB_CTRL */
+#define BIT_MARGIN_MODE_EN ( BIT(1) )
+#define BIT_DOUBLE_BIT_DISABLE ( BIT(0) )
+
+/* vars definitions for controller REGS_EFUSE */
+#define EFUSE_MAGIC_NUMBER ( 0x8810 )
+#define EFUSE_DEB_MAGIC_NUMBER ( 0x6868 )
+#define BIT_EFUSE_PROT_LOCK ( BIT(31) )
+
+#endif /* __REGS_EFUSE_H__ */
diff --git a/drivers/misc/sprd_otp/ana_efuse.c b/drivers/misc/sprd_otp/ana_efuse.c
new file mode 100644
index 00000000..6e344f61
--- /dev/null
+++ b/drivers/misc/sprd_otp/ana_efuse.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications 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.
+ *
+ */
+
+/* IMPORTANT:
+ *
+ * TODO:
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+/* FIXME: */
+#include <soc/sprd/arch_lock.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/sci.h>
+
+#include "sprd_otp.h"
+#include "__regs_ana_efuse.h"
+
+#define ANA_REGS_EFUSE_BASE ( SPRD_ADISLAVE_BASE + 0x200 )
+
+#define EFUSE_BLOCK_MAX ( 32 )
+#define EFUSE_BLOCK_WIDTH ( 8 ) /* bit counts */
+
+static DEFINE_MUTEX(adie_efuse_mtx);
+
+void adie_efuse_workaround(void)
+{
+#define REG_ADI_GSSI_CFG0 (SPRD_ADI_PHYS + 0x1C)
+/** BIT_RF_GSSI_SCK_ALL_IN
+ * 0: sclk auto gate
+ * 1: sclk always on
+*/
+#define BIT_RF_GSSI_SCK_ALL_IN BIT(30)
+ sci_glb_write(REG_ADI_GSSI_CFG0,
+ BIT_RF_GSSI_SCK_ALL_IN, BIT_RF_GSSI_SCK_ALL_IN);
+}
+
+static void adie_efuse_lock(void)
+{
+ mutex_lock(&adie_efuse_mtx);
+}
+
+static void adie_efuse_unlock(void)
+{
+ mutex_unlock(&adie_efuse_mtx);
+}
+
+static void adie_efuse_reset(void)
+{
+}
+
+static void __adie_efuse_power_on(void)
+{
+ sci_adi_set(ANA_REG_GLB_ARM_MODULE_EN, BIT_ANA_EFS_EN);
+ /* FIXME: rtc_efs only for prog
+ sci_adi_set(ANA_REG_GLB_RTC_CLK_EN, BIT_RTC_EFS_EN);
+ */
+
+ /* FIXME: sclk always on or not ? */
+ /* adie_efuse_workaround(); */
+}
+
+static void __adie_efuse_power_off(void)
+{
+ /* FIXME: rtc_efs only for prog
+ sci_adi_clr(ANA_REG_GLB_RTC_CLK_EN, BIT_RTC_EFS_EN);
+ */
+ sci_adi_clr(ANA_REG_GLB_ARM_MODULE_EN, BIT_ANA_EFS_EN);
+}
+
+static __inline int __adie_efuse_wait_clear(u32 bits)
+{
+ int ret = 0;
+ unsigned long timeout;
+
+ pr_debug("wait %x\n", sci_adi_read(ANA_REG_EFUSE_STATUS));
+
+ /* wait for maximum of 3000 msec */
+ timeout = jiffies + msecs_to_jiffies(3000);
+ while (sci_adi_read(ANA_REG_EFUSE_STATUS) & bits) {
+ if (time_after(jiffies, timeout)) {
+ WARN_ON(1);
+ ret = -ETIMEDOUT;
+ break;
+ }
+ cpu_relax();
+ }
+ return ret;
+}
+
+static u32 adie_efuse_read(int blk_index)
+{
+ u32 val = 0;
+
+ pr_debug("adie efuse read %d\n", blk_index);
+ adie_efuse_lock();
+ __adie_efuse_power_on();
+ /* enable adie_efuse module clk and power before */
+
+ /* FIXME: set read timing, why 0x20 (default value)
+ sci_adi_raw_write(ANA_REG_EFUSE_RD_TIMING_CTRL,
+ BITS_EFUSE_RD_TIMING(0x20));
+ */
+
+ sci_adi_raw_write(ANA_REG_EFUSE_BLOCK_INDEX,
+ BITS_READ_WRITE_INDEX(blk_index));
+ sci_adi_raw_write(ANA_REG_EFUSE_MODE_CTRL, BIT_RD_START);
+
+ if (IS_ERR_VALUE(__adie_efuse_wait_clear(BIT_READ_BUSY)))
+ goto out;
+
+ val = sci_adi_read(ANA_REG_EFUSE_DATA_RD);
+
+ /* FIXME: reverse the otp value */
+ val = BITS_EFUSE_DATA_RD(~val);
+
+out:
+ __adie_efuse_power_off();
+ adie_efuse_unlock();
+ return val;
+}
+
+static struct sprd_otp_operations adie_efuse_ops = {
+ .reset = adie_efuse_reset,
+ .read = adie_efuse_read,
+};
+
+u32 __adie_efuse_read(int blk_index)
+{
+ return adie_efuse_ops.read(blk_index);
+}
+
+EXPORT_SYMBOL_GPL(__adie_efuse_read);
+
+u32 __adie_efuse_read_bits(int bit_index, int length)
+{
+ int i, blk_index = (int)bit_index / EFUSE_BLOCK_WIDTH;
+ int blk_max = DIV_ROUND_UP(bit_index + length, EFUSE_BLOCK_WIDTH);
+ u32 val = 0;
+ pr_debug("otp read blk %d - %d\n", blk_index, blk_max);
+ /* FIXME: length should not bigger than 8 */
+ for (i = blk_index; i < blk_max; i++) {
+ val |= __adie_efuse_read(i)
+ << ((i - blk_index) * EFUSE_BLOCK_WIDTH);
+ }
+ //pr_debug("val=%08x\n", val);
+ val >>= (bit_index & (EFUSE_BLOCK_WIDTH - 1));
+ val &= BIT(length) - 1;
+ pr_debug("otp read bits %d ++ %d 0x%08x\n\n", bit_index, length, val);
+ return val;
+}
+EXPORT_SYMBOL_GPL(__adie_efuse_read_bits);
+
+static ssize_t adie_efuse_block_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "adie efuse blocks: %uX%ubits\n",
+ EFUSE_BLOCK_MAX, EFUSE_BLOCK_WIDTH);
+}
+
+static ssize_t adie_efuse_block_dump(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i, idx;
+ char *p = buf;
+
+ p += sprintf(p, "adie efuse blocks dump:\n");
+ p += sprintf(p, " 7 6 5 4 3 2 1 0");
+ for (idx = 0; idx < EFUSE_BLOCK_MAX; idx++) {
+ u32 val = __adie_efuse_read(idx);
+ p += sprintf(p, "\n%02d ", idx);
+ for (i = EFUSE_BLOCK_WIDTH - 1; i >= 0; --i)
+ p += sprintf(p, "%s ", (val & BIT(i)) ? "1" : "0");
+ }
+ p += sprintf(p, "\n");
+ return p - buf;
+}
+
+static DEVICE_ATTR(block, S_IRUGO, adie_efuse_block_show, NULL);
+static DEVICE_ATTR(dump, S_IRUGO, adie_efuse_block_dump, NULL);
+
+int __init sprd_adie_efuse_init(void)
+{
+ int ret;
+ void *dev;
+ dev =
+ sprd_otp_register("sprd_adie_efuse_otp", &adie_efuse_ops,
+ EFUSE_BLOCK_MAX, EFUSE_BLOCK_WIDTH / 8);
+ if (IS_ERR_OR_NULL(dev))
+ return PTR_ERR(dev);
+
+ ret = device_create_file(dev, &dev_attr_block);
+ ret |= device_create_file(dev, &dev_attr_dump);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/misc/sprd_otp/core.c b/drivers/misc/sprd_otp/core.c
new file mode 100644
index 00000000..be87085b
--- /dev/null
+++ b/drivers/misc/sprd_otp/core.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications 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.
+ *
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/bug.h>
+#include <linux/debugfs.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include "sprd_otp.h"
+
+/* cached otp blocks max 32X32 */
+#define CACHED_BLOCK_MAX ( 32 )
+#define CACHED_BLOCK_WIDTH ( 32 ) /* bit counts */
+static u8 cached_otp[CACHED_BLOCK_MAX * CACHED_BLOCK_WIDTH / 8];
+
+/*
+ * Head entry for the doubly linked sprd otp list
+ */
+static LIST_HEAD(sprd_otp_list);
+static DEFINE_MUTEX(sprd_otp_mtx);
+
+static struct sprd_otp_device *__sprd_otp_find(int minor)
+{
+ struct sprd_otp_device *otp;
+
+ if (unlikely(list_empty(&sprd_otp_list)))
+ return NULL;
+
+ list_for_each_entry(otp, &sprd_otp_list, list) {
+ if (otp->misc.minor == minor) {
+ return otp;
+ }
+ }
+ return NULL;
+}
+
+static ssize_t sprd_otp_read(struct file *file, char __user * buf,
+ size_t count, loff_t * f_pos)
+{
+ int ret;
+ int minor = iminor(file_inode(file));
+ struct sprd_otp_device *otp = __sprd_otp_find(minor);
+ mutex_lock(&sprd_otp_mtx);
+ if (otp && otp->ops && otp->ops->read) {
+ int i, blk_index = (int)*f_pos / otp->blk_width;
+ int blk_max = blk_index + DIV_ROUND_UP(count, otp->blk_width);
+ pr_debug("otp read blk %d - %d\n", blk_index, blk_max);
+ if (blk_max > otp->blk_max)
+ blk_max = otp->blk_max;
+ for (i = blk_index; i < blk_max; i++) {
+ int index = i * otp->blk_width;
+ u32 val = otp->ops->read(i);
+ memcpy(&cached_otp[index], &val, otp->blk_width);
+ }
+ }
+ pr_debug("otp read from %d %u %d\n", (int)*f_pos, count,
+ (int)file->f_pos);
+ ret =
+ simple_read_from_buffer(buf, count, f_pos, cached_otp,
+ sizeof(cached_otp));
+
+ mutex_unlock(&sprd_otp_mtx);
+ return ret;
+}
+
+static ssize_t sprd_otp_write(struct file *file, const char __user * buf,
+ size_t count, loff_t * f_pos)
+{
+ ssize_t ret;
+ int pos = (int)*f_pos;
+ int minor = iminor(file_inode(file));
+ struct sprd_otp_device *otp = __sprd_otp_find(minor);
+ pr_debug("otp write to %d %u %d\n", (int)*f_pos, count,
+ (int)file->f_pos);
+ mutex_lock(&sprd_otp_mtx);
+ ret = simple_write_to_buffer(cached_otp, sizeof(cached_otp), f_pos,
+ buf, count);
+ if (IS_ERR_VALUE(ret))
+ goto out;
+
+ if (otp && otp->ops && otp->ops->prog) {
+ int blk_index = pos / otp->blk_width;
+ pr_debug("otp prog blk %d\n", blk_index);
+ BUG_ON(count != otp->blk_width);
+ if (blk_index < otp->blk_max) {
+ int index = blk_index * otp->blk_width;
+ u32 val = 0;
+ memcpy(&val, &cached_otp[index], otp->blk_width);
+ otp->ops->prog(blk_index, val);
+ }
+ }
+out:
+ mutex_unlock(&sprd_otp_mtx);
+ return ret;
+}
+
+static loff_t sprd_otp_llseek(struct file *file, loff_t offset, int whence)
+{
+ pr_debug("otp seek at %d %d\n", (int)offset, whence);
+ switch (whence) {
+ case SEEK_SET:
+ file->f_pos = offset;
+ break;
+ case SEEK_CUR:
+ file->f_pos += offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return file->f_pos;
+}
+
+static const struct file_operations sprd_otp_fops = {
+ .owner = THIS_MODULE,
+ .read = sprd_otp_read,
+ .write = sprd_otp_write,
+ .llseek = sprd_otp_llseek,
+};
+
+static struct miscdevice dummy_otp = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "sprd_dummy_otp",
+ .fops = &sprd_otp_fops,
+};
+
+void *sprd_otp_register(const char *name, void *ops, int blk_max, int blk_width)
+{
+ int ret;
+ struct sprd_otp_device *otp;
+ otp = kzalloc(sizeof(struct sprd_otp_device), GFP_KERNEL);
+ if (unlikely(!otp))
+ return ERR_PTR(-ENOMEM);
+ INIT_LIST_HEAD(&otp->list);
+ otp->misc.minor = MISC_DYNAMIC_MINOR;
+ otp->misc.name = kstrdup(name, GFP_KERNEL);
+ otp->misc.fops = &sprd_otp_fops;
+ otp->blk_max = blk_max;
+ otp->blk_width = blk_width; /*MUST be bytes count */
+ BUG_ON(otp->blk_width > sizeof(u32));
+ BUG_ON(otp->blk_max * otp->blk_width > sizeof(cached_otp));
+ otp->ops = ops;
+ ret = misc_register(&otp->misc);
+ if (ret)
+ return ERR_PTR(ret);
+ mutex_lock(&sprd_otp_mtx);
+ list_add(&otp->list, &sprd_otp_list);
+ mutex_unlock(&sprd_otp_mtx);
+ pr_info("otp register %s as miscdevice %d, block %uX%ubits\n",
+ otp->misc.name, otp->misc.minor, otp->blk_max,
+ otp->blk_width * 8);
+ return otp->misc.this_device;
+}
+
+static ssize_t otp_info_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "cached otp blocks: %uX%ubits\n",
+ CACHED_BLOCK_MAX, CACHED_BLOCK_WIDTH);
+}
+
+static DEVICE_ATTR(info, S_IRUGO, otp_info_show, NULL);
+
+static int __init sprd_otp_init(void)
+{
+ int ret;
+ ret = misc_register(&dummy_otp);
+ if (ret)
+ return ret;
+
+ ret = device_create_file(dummy_otp.this_device, &dev_attr_info);
+ if (ret)
+ return ret;
+
+#if defined(CONFIG_OTP_SPRD_EFUSE)
+ sprd_efuse_init();
+#endif
+#if defined(CONFIG_OTP_SPRD_ADIE_EFUSE)
+ sprd_adie_efuse_init();
+#endif
+#if defined(CONFIG_OTP_SPRD_LASER_FUSE)
+ sprd_adie_laserfuse_init();
+#endif
+ return ret;
+}
+
+static void __exit sprd_otp_exit(void)
+{
+ misc_deregister(&dummy_otp);
+}
+
+module_init(sprd_otp_init);
+module_exit(sprd_otp_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Spreadtrum OTP Efuse Driver");
+MODULE_AUTHOR("robot <zhulin.lian@spreadtrum.com>");
+MODULE_VERSION("0.1");
diff --git a/drivers/misc/sprd_otp/efuse.c b/drivers/misc/sprd_otp/efuse.c
new file mode 100644
index 00000000..f1bb18c2
--- /dev/null
+++ b/drivers/misc/sprd_otp/efuse.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications 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.
+ *
+ */
+
+/* IMPORTANT:
+ * The electrical fuse is a type of non-volatile memory fabricated
+ * in standard CMOS logic process. This electrical fuse macro is widely
+ * used in chip ID, memory redundancy, security code, configuration setting,
+ * and feature selection, etc.
+ *
+ * This efuse controller is designed for 32*32 bits electrical fuses,
+ * support TSMC HPM 28nm product of "TEF28HPM32X32HD18_PHRM".
+ *
+ * and we default use double-bit, 512bits efuse are visiable for software.
+ *
+ * efuse driver ONLY support module ip version since from r3p0
+ * which had integrated into scx30g and scx35l etc.
+ *
+ * TODO:
+ * 1. do something when block had been locked with bit31 if need
+ * 1. check and clear blk prog/err flag if need
+ * 1. wait *300ms* for read/prog ready time-out
+ * 1. only mutexlock and hwspinlock for efuse access sync with cp
+ * 1. be care not do use efuse API in interrupt function
+ * 1. no need soft reset after efuse read/prog and power on/off
+ * 1. efuse block width should less than 8bits
+ * 1. efuse block count should not bigger than 32! or not should expland the cached otp arrary
+ * 1. support efuse DT info (version, blocks, ...) later
+ * 1. there is no handle for efuse module removed
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+/* FIXME: */
+#include <soc/sprd/arch_lock.h>
+
+#ifdef CONFIG_64BIT
+#define SPRD_UIDEFUSE_BASE SPRD_DEV_P2V(0x40240000)
+#endif
+
+#include "sprd_otp.h"
+#include "__regs_efuse.h"
+
+#define REGS_EFUSE_BASE SPRD_UIDEFUSE_BASE
+
+#define EFUSE_BLOCK_MAX ( 16 )
+#define EFUSE_BLOCK_WIDTH ( 32 ) /* bit counts */
+
+static u32 efuse_magic;
+static DEFINE_MUTEX(efuse_mtx);
+static void efuse_lock(void)
+{
+ mutex_lock(&efuse_mtx);
+ arch_hwlock_fast(HWLOCK_EFUSE);
+}
+
+static void efuse_unlock(void)
+{
+ arch_hwunlock_fast(HWLOCK_EFUSE);
+ mutex_unlock(&efuse_mtx);
+}
+
+static void efuse_reset(void)
+{
+ /* should enable module before soft reset efuse */
+ WARN_ON(!sci_glb_read(REG_AON_APB_APB_EB0, BIT_EFUSE_EB));
+ sci_glb_set(REG_AON_APB_APB_RST0, BIT_EFUSE_SOFT_RST);
+ udelay(5);
+ sci_glb_clr(REG_AON_APB_APB_RST0, BIT_EFUSE_SOFT_RST);
+}
+
+/* FIXME: Set EFS_VDD_ON will open 0.9v static power supply for efuse memory,
+ * before any operation towards to efuse memory this bit have to set to 1.
+ * Once this bit is cleared, the efuse will go to power down mode.
+ *
+ * each time when EFS_VDD_ON changes, software should wait at least 1ms to let
+ * VDD become stable.
+ *
+ * For VDDQ(1.8v) power, to prevent the overshot of VDDQ, a extra power switch
+ * connected to ground are controlled by "EFS_VDDQ_K2_ON"
+ */
+void __efuse_prog_power_on(void)
+{
+ u32 cfg0;
+ sci_glb_set(REG_AON_APB_APB_EB0, BIT_EFUSE_EB);
+ cfg0 = __raw_readl((void *)REG_EFUSE_CFG0);
+ cfg0 &= ~(BIT_EFS_VDDQ_K2_ON | BIT_EFS_VDDQ_K1_ON);
+ cfg0 |= BIT_EFS_VDD_ON;
+ __raw_writel(cfg0, (void *)REG_EFUSE_CFG0);
+ msleep(1);
+
+ cfg0 |= BIT_EFS_VDDQ_K1_ON;
+ __raw_writel(cfg0, (void *)REG_EFUSE_CFG0);
+ msleep(1);
+}
+
+void __efuse_power_on(void)
+{
+ u32 cfg0;
+ sci_glb_set(REG_AON_APB_APB_EB0, BIT_EFUSE_EB);
+ cfg0 = __raw_readl((void *)REG_EFUSE_CFG0);
+ cfg0 &= ~BIT_EFS_VDDQ_K1_ON;
+ cfg0 |= BIT_EFS_VDD_ON | BIT_EFS_VDDQ_K2_ON;
+ __raw_writel(cfg0, (void *)REG_EFUSE_CFG0);
+ msleep(1);
+}
+
+void __efuse_power_off(void)
+{
+ u32 cfg0 = __raw_readl((void *)REG_EFUSE_CFG0);
+ if (cfg0 & BIT_EFS_VDDQ_K1_ON) {
+ cfg0 &= ~BIT_EFS_VDDQ_K1_ON;
+ __raw_writel(cfg0, (void *)REG_EFUSE_CFG0);
+ msleep(1);
+ }
+
+ cfg0 |= BIT_EFS_VDDQ_K2_ON;
+ cfg0 &= ~BIT_EFS_VDD_ON;
+ __raw_writel(cfg0, (void *)REG_EFUSE_CFG0);
+ msleep(1);
+
+ sci_glb_clr(REG_AON_APB_APB_EB0, BIT_EFUSE_EB);
+}
+
+static __inline int __efuse_wait_clear(u32 bits)
+{
+ int ret = 0;
+ unsigned long timeout;
+
+ pr_debug("wait %x\n", __raw_readl((void *)REG_EFUSE_STATUS));
+
+ /* wait for maximum of 300 msec */
+ timeout = jiffies + msecs_to_jiffies(300);
+ while (__raw_readl((void *)REG_EFUSE_STATUS) & bits) {
+ if (time_after(jiffies, timeout)) {
+ WARN_ON(1);
+ ret = -ETIMEDOUT;
+ break;
+ }
+ cpu_relax();
+ }
+ return ret;
+}
+
+static u32 __efuse_read(int blk)
+{
+ u32 val = 0;
+
+ /* enable efuse module clk and power before */
+
+ __raw_writel(BITS_READ_WRITE_INDEX(blk),
+ (void *)REG_EFUSE_READ_WRITE_INDEX);
+ __raw_writel(BIT_RD_START, (void *)REG_EFUSE_MODE_CTRL);
+
+ if (IS_ERR_VALUE(__efuse_wait_clear(BIT_READ_BUSY)))
+ goto out;
+
+ val = __raw_readl((void *)REG_EFUSE_DATA_RD);
+
+out:
+ return val;
+}
+
+static u32 efuse_read(int blk_index)
+{
+ u32 val;
+ pr_debug("efuse read %d\n", blk_index);
+ efuse_lock();
+
+ __efuse_power_on();
+ val = __efuse_read(blk_index);
+ __efuse_power_off();
+
+ efuse_unlock();
+ return val;
+}
+
+static int __efuse_prog(int blk, u32 val)
+{
+ u32 cfg0 = __raw_readl((void *)REG_EFUSE_CFG0);
+
+ if (blk < 0 || blk >= EFUSE_BLOCK_MAX) /* debug purpose */
+ goto out;
+
+ /* enable pgm mode and setup magic number before programming */
+ cfg0 |= BIT_PGM_EN;
+ __raw_writel(cfg0, (void *)REG_EFUSE_CFG0);
+ __raw_writel(BITS_MAGIC_NUMBER(efuse_magic),
+ (void *)REG_EFUSE_MAGIC_NUMBER);
+
+ __raw_writel(val, (void *)REG_EFUSE_DATA_WR);
+ __raw_writel(BITS_READ_WRITE_INDEX(blk),
+ (void *)REG_EFUSE_READ_WRITE_INDEX);
+ pr_debug("cfg0 %x\n", __raw_readl((void *)REG_EFUSE_CFG0));
+ __raw_writel(BIT_PG_START, (void *)REG_EFUSE_MODE_CTRL);
+ if (IS_ERR_VALUE(__efuse_wait_clear(BIT_PGM_BUSY)))
+ goto out;
+
+out:
+ __raw_writel(0, (void *)REG_EFUSE_MAGIC_NUMBER);
+ cfg0 &= ~BIT_PGM_EN;
+ __raw_writel(cfg0, (void *)REG_EFUSE_CFG0);
+ return 0;
+}
+
+static int efuse_prog(int blk_index, u32 val)
+{
+ int ret;
+ pr_debug("efuse prog %d %08x\n", blk_index, val);
+
+ efuse_lock();
+
+ /* enable vddon && vddq */
+ __efuse_prog_power_on();
+ ret = __efuse_prog(blk_index, val);
+ __efuse_power_off();
+
+ efuse_unlock();
+ return ret;
+}
+
+static struct sprd_otp_operations efuse_ops = {
+ .reset = efuse_reset,
+ .read = efuse_read,
+ .prog = efuse_prog,
+};
+
+u32 __ddie_efuse_read(int blk_index)
+{
+ return efuse_ops.read(blk_index);
+}
+
+EXPORT_SYMBOL_GPL(__ddie_efuse_read);
+
+static ssize_t efuse_block_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "ddie efuse blocks: %uX%ubits\nprog magic: 0x%x\n",
+ EFUSE_BLOCK_MAX, EFUSE_BLOCK_WIDTH, efuse_magic);
+}
+
+static ssize_t efuse_magic_store(struct device *pdev,
+ struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ sscanf(buff, "%x", &efuse_magic);
+ return size;
+}
+
+static ssize_t efuse_block_dump(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int idx;
+ char *p = buf;
+
+ p += sprintf(p, "ddie efuse blocks dump:\n");
+ for (idx = 0; idx < EFUSE_BLOCK_MAX; idx++) {
+ p += sprintf(p, "[%02d] %08x\n", idx, efuse_read(idx));
+ }
+ return p - buf;
+}
+#ifdef CONFIG_ARCH_SCX20
+static ssize_t efuse_uid_info(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u32 block0,block1;
+ u32 x,y,wafer_id;
+ u32 LOTID_0,LOTID_1,LOTID_2,LOTID_3,LOTID_4,LOTID_5;
+ char *p = buf;
+ block0= efuse_read(0);
+ block1= efuse_read(1);
+ y=block1&0x7F;
+ x=(block1>>7)&0x7F;
+ wafer_id=(block1>>14)&0x1F;
+ LOTID_0=(block1>>19)&0x3F;
+ LOTID_1=(block1>>25)&0x3F;
+ LOTID_2=block0&0x3F;
+ LOTID_3=(block0>>6)&0x3F;
+ LOTID_4=(block0>>12)&0x3F;
+ LOTID_5=(block0>>18)&0x3F;
+ p += sprintf(p, "efuse uid dump:\n");
+ p += sprintf(p,"%c%c%c%c%c%c_%d_%d_%d\n",LOTID_5+48,LOTID_4+48,LOTID_3+48,LOTID_2+48,LOTID_1+48,LOTID_0+48,wafer_id,x,y);
+ p += sprintf(p, "\n");
+ return p - buf;
+}
+
+static void efuse_uid(void)
+{
+ u32 block0,block1;
+ u32 x,y,wafer_id;
+ u32 LOTID_0,LOTID_1,LOTID_2,LOTID_3,LOTID_4,LOTID_5;
+ char bufallid[20];
+ block0= efuse_read(0);
+ block1= efuse_read(1);
+ y=block1&0x7F;
+ x=(block1>>7)&0x7F;
+ wafer_id=(block1>>14)&0x1F;
+ LOTID_0=(block1>>19)&0x3F;
+ LOTID_1=(block1>>25)&0x3F;
+ LOTID_2=block0&0x3F;
+ LOTID_3=(block0>>6)&0x3F;
+ LOTID_4=(block0>>12)&0x3F;
+ LOTID_5=(block0>>18)&0x3F;
+ pr_info("uid is %c%c%c%c%c%c_%d_%d_%d\n",LOTID_5+48,LOTID_4+48,LOTID_3+48,LOTID_2+48,LOTID_1+48,LOTID_0+48,wafer_id,x,y);
+}
+static DEVICE_ATTR(uid, S_IRUGO, efuse_uid_info, NULL);
+#endif
+static DEVICE_ATTR(block, S_IRUGO, efuse_block_show, NULL);
+static DEVICE_ATTR(magic, S_IWUSR, NULL, efuse_magic_store);
+static DEVICE_ATTR(dump, S_IRUGO, efuse_block_dump, NULL);
+
+
+
+int __init sprd_efuse_init(void)
+{
+ int ret;
+ void *dev;
+ dev =
+ sprd_otp_register("sprd_efuse_otp", &efuse_ops, EFUSE_BLOCK_MAX,
+ EFUSE_BLOCK_WIDTH / 8);
+ if (IS_ERR_OR_NULL(dev))
+ return PTR_ERR(dev);
+#ifdef CONFIG_ARCH_SCX20
+ efuse_uid();
+#endif
+ ret = device_create_file(dev, &dev_attr_block);
+ ret |= device_create_file(dev, &dev_attr_magic);
+ ret |= device_create_file(dev, &dev_attr_dump);
+#ifdef CONFIG_ARCH_SCX20
+ ret |= device_create_file(dev, &dev_attr_uid);
+#endif
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/misc/sprd_otp/laser_fuse.c b/drivers/misc/sprd_otp/laser_fuse.c
new file mode 100644
index 00000000..4df8a897
--- /dev/null
+++ b/drivers/misc/sprd_otp/laser_fuse.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications 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.
+ *
+ */
+
+/* IMPORTANT:
+ *
+ * TODO:
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <soc/sprd/arch_lock.h>
+
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/sci_glb_regs.h>
+
+#include "sprd_otp.h"
+
+#define EFUSE_BLOCK_MAX ( 4 )
+#define EFUSE_BLOCK_WIDTH ( 16 ) /* bit counts */
+
+static DEFINE_MUTEX(afuse_mtx);
+static void afuse_lock(void)
+{
+ mutex_lock(&afuse_mtx);
+ arch_hwlock_fast(HWLOCK_EFUSE);
+}
+
+static void afuse_unlock(void)
+{
+ arch_hwunlock_fast(HWLOCK_EFUSE);
+ mutex_unlock(&afuse_mtx);
+}
+
+static u32 afuse_read(int blk)
+{
+ unsigned long timeout;
+ u32 val = 0;
+
+ pr_debug("adie laser_fuse read %d\n", blk);
+ afuse_lock();
+
+ if (blk < 0 || blk >= EFUSE_BLOCK_MAX) /* debug purpose */
+ goto out;
+
+ sci_adi_write_fast(ANA_REG_GLB_AFUSE_CTRL, BIT_AFUSE_READ_REQ, 1);
+ udelay(1);
+ /* wait for maximum of 300 msec */
+ timeout = jiffies + msecs_to_jiffies(300);
+ while (BIT_AFUSE_READ_REQ & sci_adi_read(ANA_REG_GLB_AFUSE_CTRL)) {
+ if (time_after(jiffies, timeout)) {
+ WARN_ON(1);
+ goto out;
+ }
+ cpu_relax();
+ }
+ val = sci_adi_read(ANA_REG_GLB_AFUSE_OUT0 + 4 * blk);
+
+out:
+ afuse_unlock();
+ return val;
+}
+
+#define BITS_AFUSE_DLY_PROT_KEY (0xa2 << 8)
+static void afuse_set_readdly(u32 dly)
+{
+ u32 val = BITS_AFUSE_DLY_PROT_KEY + BITS_AFUSE_READ_DLY(dly);
+ afuse_lock();
+
+ sci_adi_write_fast(ANA_REG_GLB_AFUSE_CTRL, val, 0);
+ val &= ~BITS_AFUSE_DLY_PROT_KEY; /*release lock */
+ sci_adi_write_fast(ANA_REG_GLB_AFUSE_CTRL, val, 1);
+
+ afuse_unlock();
+}
+
+static struct sprd_otp_operations afuse_ops = {
+ .read = afuse_read,
+};
+
+u32 __adie_laserfuse_read(int blk_index)
+{
+ return afuse_ops.read(blk_index);
+}
+
+EXPORT_SYMBOL_GPL(__adie_laserfuse_read);
+
+static ssize_t afuse_block_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "adie laser_fuse blocks: %uX%ubits\n",
+ EFUSE_BLOCK_MAX, EFUSE_BLOCK_WIDTH);
+}
+
+static ssize_t afuse_readdly_store(struct device *pdev,
+ struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ u32 dly;
+ sscanf(buff, "%x", &dly);
+ afuse_set_readdly(dly);
+ return size;
+}
+
+static ssize_t afuse_block_dump(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int idx;
+ char *p = buf;
+
+ p += sprintf(p, "adie laser_fuse blocks dump:\n");
+ for (idx = 0; idx < EFUSE_BLOCK_MAX; idx++) {
+ p += sprintf(p, "[%02d] %08x\n", idx,
+ __adie_laserfuse_read(idx));
+ }
+ return p - buf;
+}
+
+static DEVICE_ATTR(block, S_IRUGO, afuse_block_show, NULL);
+static DEVICE_ATTR(readdly, S_IWUSR, NULL, afuse_readdly_store);
+static DEVICE_ATTR(dump, S_IRUGO, afuse_block_dump, NULL);
+
+int __init sprd_adie_laserfuse_init(void)
+{
+ int ret;
+ void *dev;
+ dev =
+ sprd_otp_register("sprd_afuse_otp", &afuse_ops,
+ EFUSE_BLOCK_MAX, EFUSE_BLOCK_WIDTH / 8);
+ if (IS_ERR_OR_NULL(dev))
+ return PTR_ERR(dev);
+
+ ret = device_create_file(dev, &dev_attr_block);
+ ret |= device_create_file(dev, &dev_attr_readdly);
+ ret |= device_create_file(dev, &dev_attr_dump);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/misc/sprd_otp/otp-bitmap.c b/drivers/misc/sprd_otp/otp-bitmap.c
new file mode 100644
index 00000000..d3e64c2d
--- /dev/null
+++ b/drivers/misc/sprd_otp/otp-bitmap.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications 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.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include "sprd_otp.h"
+
+u32 __adie_efuse_read_bits(int bit_index, int length);
+
+struct otp_bitmap {
+ const char *name; /* MUST same as sc2723-regulators.dtsi */
+ int bits_idx, bits_len;/* field bits offset and length */
+};
+
+const struct otp_bitmap otp_bitmap[] = {
+ /* regulator name, block, offset, length */
+ //DCDC (6)
+ {"vddcore", BITSINDEX(0, 0), 6}, /* DCDC_CORE_NOR */
+ {"vddcore_ds", BITSINDEX(1, 0), 6}, /* DCDC_CORE_SLP */
+ {"vddarm", BITSINDEX(1, 6), 7}, /* DCDC_ARM_A */
+ {"vddarm_b", BITSINDEX(2, 5), 7}, /* DCDC_ARM_B */
+ {"vddmem", BITSINDEX(3, 6), 7}, /* DCDC_MEM */
+ {"vddgen", BITSINDEX(4, 5), 7}, /* DCDC_GEN */
+ {"vddrf", BITSINDEX(5, 4), 7},
+ {"vddcon", BITSINDEX(13, 1), 7}, /* DCDC_CON?DCDC_WPA? */
+
+ //LDO (20)
+ {"vdd25", BITSINDEX(18, 2), 6}, /* AVDD28? *//* FIXME: be care LDO_VDD2V5_OPT 0 : 1.8v, 1 : 2.8V */
+ {"vddkpled", BITSINDEX(19, 0), 6},
+ {"vddvibr", BITSINDEX(19, 6), 6},
+ {"vddwifipa", BITSINDEX(20, 4), 6},
+ {"vddcammot", BITSINDEX(21, 2), 6},
+ {"vddemmccore", BITSINDEX(22, 0), 6},
+ {"vdddcxo", BITSINDEX(22, 6), 6},
+ {"vddsdcore", BITSINDEX(23, 4), 6},
+ {"vdd28", BITSINDEX(24, 2), 6},
+ {"vddcama", BITSINDEX(25, 0), 6},
+ {"vddsim0", BITSINDEX(25, 6), 6},
+ {"vddsim1", BITSINDEX(25, 6), 6}, /* share with vddsim0 */
+ {"vddsim2", BITSINDEX(25, 6), 6}, /* share with vddsim0 */
+ {"vddsdio", BITSINDEX(26, 4), 6},
+ {"vddusb", BITSINDEX(27, 2), 6},
+ {"vddcamio", BITSINDEX(28, 0), 6},
+ {"vdd18", BITSINDEX(28, 0), 6}, /* share with vddcamio */
+ {"vddcamd", BITSINDEX(28, 6), 6},
+ {"vddrf0", BITSINDEX(29, 4), 6},
+ {"vddgen1", BITSINDEX(30, 2), 6},
+ {"vddgen0", BITSINDEX(31, 0), 6},
+ {0},
+};
+
+int sci_otp_get_offset(const char *name)
+{
+ const struct otp_bitmap *map = otp_bitmap;
+ int offset = 0;
+ for (; map->name; map++) {
+ if (0 == strcmp(map->name, name)) {
+ offset -= (int)(BIT(map->bits_len) / 2); /* middle */
+ offset +=
+ (int)__adie_efuse_read_bits(map->bits_idx,
+ map->bits_len);
+ }
+ }
+ if ((0 == strcmp(name, "vddcore"))
+ || (0 == strcmp(name, "vddarm"))) {
+ int bb_chip = (int)__adie_efuse_read_bits(BITSINDEX(31, 6), 2);
+ pr_debug("%s bb_chip magic %d\n", name, bb_chip);
+ offset += -32 + 32 * bb_chip;
+ }
+ pr_debug("%s offset %d (steps)\n", name, offset);
+ return offset;
+}
diff --git a/drivers/misc/sprd_otp/otp-helper.c b/drivers/misc/sprd_otp/otp-helper.c
new file mode 100644
index 00000000..743dc6e0
--- /dev/null
+++ b/drivers/misc/sprd_otp/otp-helper.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications 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.
+ *
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/err.h>
+#include "sprd_otp.h"
+
+#define BLK_UID_HIGH ( 0 )
+#define BLK_UID_LOW ( 1 )
+#define BLK_ADC_DETA ( 7 )
+#define BLK_CCCV_DETA ( 9 )
+
+#define BLK_FGU_DETA_ABC ( 8 )
+#define BLK_FGU_DETA_D ( 9 )
+
+#define BLK_WIDTH_OTP_EMEMORY ( 8 ) /* bit counts */
+#define BLK_ADC_DETA_ABC_OTP ( 7 ) /* start block for ADC otp delta */
+
+u32 __weak __ddie_efuse_read(int blk_index)
+{
+ return 0;
+}
+/*---------------------------------------------------------------------------*
+ * Efuse public API functions for other kernel modules *
+ *---------------------------------------------------------------------------*/
+u64 sci_efuse_get_uid(void)
+{
+ u64 uid_hi, uid_lo;
+
+ uid_hi = __ddie_efuse_read(BLK_UID_HIGH);
+ uid_lo = __ddie_efuse_read(BLK_UID_LOW);
+
+ /* fill system serial number */
+ return ((uid_hi << 32ull) | uid_lo);
+}
+
+EXPORT_SYMBOL_GPL(sci_efuse_get_uid);
+
+#if defined(CONFIG_ARCH_SCX20L) || defined(CONFIG_ARCH_SCX35L64)\
+|| defined(CONFIG_ARCH_SCX20)
+#define BLK_THM_DETA (13)
+#define THM_CAL_BIT (26)
+#else
+#define BLK_THM_DETA (7)
+#define THM_CAL_BIT (24)
+#endif
+int sci_efuse_thermal_cal_get(int *cal)
+{
+ u32 data=__ddie_efuse_read(BLK_THM_DETA);
+ int thm_cal = (data >> THM_CAL_BIT) & 0x001F;
+ if (thm_cal ==0) {
+ *cal = 0;
+ return -1;
+ }
+ *cal = thm_cal -16;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sci_efuse_thermal_cal_get);
+
+#define BASE_ADC_P0 711 //3.6V
+#define BASE_ADC_P1 830 //4.2V
+#define VOL_P0 3600
+#define VOL_P1 4200
+#define ADC_DATA_OFFSET 128
+int sci_efuse_calibration_get(unsigned int *p_cal_data)
+{
+ unsigned int deta = 0;
+ unsigned short adc_temp = 0;
+
+#if defined(CONFIG_ADIE_SC2723) || defined(CONFIG_ADIE_SC2723S)
+ /* verify the otp data of ememory written or not */
+ adc_temp = (__adie_efuse_read(0) & BIT(7));
+ if (adc_temp)
+ return 0;
+
+ deta = __adie_efuse_read_bits(BLK_ADC_DETA_ABC_OTP * BLK_WIDTH_OTP_EMEMORY, 16);
+#elif defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_ARCH_SCX35L)
+ deta = __ddie_efuse_read(BLK_ADC_DETA);
+ WARN_ON(!(deta & BIT(31))); /* BIT 31 is protected bit */
+#else
+ #warning "AuxADC CAL DETA need fixing"
+#endif
+
+ pr_info("%s() get efuse block %u, deta: 0x%08x\n", __func__, BLK_ADC_DETA, deta);
+
+ deta &= 0xFFFFFF; /* get BIT0 ~ BIT23) in block 7 */
+
+ if ((!deta) || (p_cal_data == NULL)) {
+ return 0;
+ }
+ //adc 3.6V
+ adc_temp = ((deta >> 8) & 0x00FF) + BASE_ADC_P0 - ADC_DATA_OFFSET;
+ p_cal_data[1] = (VOL_P0) | ((adc_temp << 2) << 16);
+
+ //adc 4.2V
+ adc_temp = (deta & 0x00FF) + BASE_ADC_P1 - ADC_DATA_OFFSET;
+ p_cal_data[0] = (VOL_P1) | ((adc_temp << 2) << 16);
+
+ return 1;
+}
+
+EXPORT_SYMBOL_GPL(sci_efuse_calibration_get);
+
+int sci_efuse_cccv_cal_get(unsigned int *p_cal_data)
+{
+#if defined(CONFIG_ADIE_SC2723) || defined(CONFIG_ADIE_SC2723S)
+ unsigned int data,blk0;
+
+ blk0 = __adie_efuse_read(0);
+
+ if (blk0 & (1 << 7)) {
+ return 0;
+ }
+
+ data = __adie_efuse_read_bits(BITSINDEX(14, 0), 6);
+ pr_info("sci_efuse_cccv_cal_get data:0x%x\n", data);
+ *p_cal_data = (data) & 0x003F;
+#else
+ unsigned int data;
+
+ data = __ddie_efuse_read(BLK_CCCV_DETA);
+ data &= ~(1 << 31);
+
+ pr_info("sci_efuse_cccv_cal_get data:0x%x\n", data);
+
+ if ((!data) || (p_cal_data == NULL)) {
+ return 0;
+ }
+
+ *p_cal_data = (data >> 24) & 0x003F; /*block 9, bit 29..24 */
+#endif
+ return 1;
+}
+
+EXPORT_SYMBOL_GPL(sci_efuse_cccv_cal_get);
+
+int sci_efuse_fgu_cal_get(unsigned int *p_cal_data)
+{
+#if defined(CONFIG_ADIE_SC2723) || defined(CONFIG_ADIE_SC2723S)
+ unsigned int data,blk0;
+
+ blk0 = __adie_efuse_read(0);
+
+ if (blk0 & (1 << 7)) {
+ return 0;
+ }
+
+ data = __adie_efuse_read_bits(BITSINDEX(12, 0), 9);
+ printk("sci_efuse_fgu_cal_get 4.2 data data:0x%x\n", data);
+ p_cal_data[0] = (data + 6963) - 4096 - 256; //4.0V or 4.2V
+
+ data = __adie_efuse_read_bits(BITSINDEX(14, 2), 6);
+ printk("sci_efuse_fgu_cal_get 3.6 data data:0x%x\n", data);
+ p_cal_data[1] = p_cal_data[0] - 410 - data + 16; //3.6V
+
+ data = __adie_efuse_read_bits(BITSINDEX(13, 1), 9);
+ printk("sci_efuse_fgu_cal_get 0 data data:0x%x\n", data);
+ p_cal_data[2] = (((data) & 0x1FF) + 8192) - 256;
+
+ return 1;
+#else
+ unsigned int data;
+
+ data = __ddie_efuse_read(BLK_FGU_DETA_ABC);
+ data &= ~(1 << 31);
+
+ pr_info("sci_efuse_fgu_cal_get ABC data:0x%x\n", data);
+
+ if ((!data) || (p_cal_data == NULL)) {
+ return 0;
+ }
+
+ p_cal_data[0] = (((data >> 16) & 0x1FF) + 6963) - 4096 - 256; //4.0V or 4.2V
+ p_cal_data[1] = p_cal_data[0] - 410 - ((data >> 25) & 0x3F) + 16; //3.6V
+ p_cal_data[2] = (((data) & 0x1FF) + 8192) - 256;
+
+ return 1;
+#endif
+}
+
+EXPORT_SYMBOL_GPL(sci_efuse_fgu_cal_get);
+
+/*
+ * sci_efuse_get_apt_cal - read apt data saved in efuse
+ * @pdata: apt data pointer for dcdcwpa (unit: uV)
+ * pdata[0] -> 3v
+ * pdata[1] -> 0.8v
+ * @num: the length of apt data
+ *
+ * retruns 0 if success, else
+ * returns negative number.
+ */
+#define APT_CAL_DATA_BLK ( 10 )
+#define OFFSET2VOL(p, uv) ( ((p) - 128) * (uv) / 256)
+int sci_efuse_get_apt_cal(unsigned int *pdata, int num)
+{
+ int i = 0;
+ u32 efuse_data = 0;
+ u8 *offset = (u8 *) & efuse_data;
+ const unsigned int vol_ref[2] = {
+ 3000000, //3v
+ 800000, //0.8v
+ };
+
+ /* Fixme: dcdcwpa only support two points(3.6v / 0.8v) voltage calibration */
+ BUG_ON(num > 2);
+
+ efuse_data = __ddie_efuse_read(APT_CAL_DATA_BLK);
+
+ pr_info("%s efuse data: 0x%08x\n", __func__, efuse_data);
+
+ WARN_ON(!(efuse_data & BIT(31))); /* BIT 31 is protected bit */
+ efuse_data &= 0xFFFF; /* get BIT0 ~ BIT15 in block 10 */
+
+ if (!(efuse_data) || (!pdata)) {
+ return -1;
+ }
+
+ printk("%s: ", __func__);
+ for (i = 0; i < num; i++) {
+ pdata[i] = (unsigned int)OFFSET2VOL(offset[i], vol_ref[i]);
+ printk("(%duV : %08d), ", vol_ref[i], pdata[i]);
+ }
+ printk("\n");
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(sci_efuse_get_apt_cal);
+
+/*
+ * sci_efuse_get_cal - read 4 point adc calibration data saved in efuse
+ * @pdata: adc data pointer
+ * pdata[0] -> 4.2v
+ * pdata[1] -> 3.6v
+ * pdata[2] -> 0.4v
+ * @num: the length of adc data
+ *
+ * retruns 0 if success, else
+ * returns negative number.
+ */
+#define BLK_ADC_DETA_ABC ( 7 )
+#define BLK_ADC_DETA_D ( 9 )
+#define DELTA2ADC(_delta_, _Ideal_) ( ((_delta_) + (_Ideal_) - 128) << 2 )
+#define ADC_VOL(_delta_, _Ideal_, vol) ( (DELTA2ADC(_delta_, _Ideal_) << 16) | (vol) )
+
+int sci_efuse_get_cal(unsigned int *pdata, int num)
+{
+ int i;
+ u32 efuse_data = 0;
+ u8 *delta = (u8 *) & efuse_data;
+ const u16 ideal[3][2] = {
+ {4200, 830}, /* FIXME: efuse only support 10bits */
+ {3600, 711},
+ {400, 79},
+ };
+
+#if defined(CONFIG_ADIE_SC2723) || defined(CONFIG_ADIE_SC2723S)
+ /* verify the otp data of ememory written or not */
+ efuse_data = (__adie_efuse_read(0) & BIT(7));
+ if (efuse_data)
+ return -2;
+
+ efuse_data = __adie_efuse_read_bits(BLK_ADC_DETA_ABC_OTP * BLK_WIDTH_OTP_EMEMORY, 16);
+#elif defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_ARCH_SCX35L)
+ efuse_data = __ddie_efuse_read(BLK_ADC_DETA_ABC);
+ WARN_ON(!(efuse_data & BIT(31))); /* BIT 31 is protected bit */
+#else
+ #warning "AuxADC CAL DETA need fixing"
+#endif
+
+ pr_info("%s efuse data: 0x%08x\n", __func__, efuse_data);
+
+ efuse_data &= 0xFFFFFF; /* get BIT0 ~ BIT23 in block 7 */
+
+ if (!(efuse_data) || (!pdata)) {
+ return -1;
+ }
+
+ //WARN_ON(!((delta[0] > delta[1]) && (delta[1] > delta[2])));
+
+ for (i = 0; i < num; i++) {
+ pdata[i] =
+ (unsigned int)ADC_VOL(delta[i], ideal[i][1], ideal[i][0]);
+ }
+
+ /* dump adc calibration parameters */
+ printk("%s adc_cal: ", __func__);
+ for (i = 0; i < num; i++) {
+ printk("%d -- %d; \n", (pdata[i] & 0xFFFF),
+ (pdata[i] >> 16) & 0xFFFF);
+ }
+ printk("\n");
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(sci_efuse_get_cal);
+
+/*
+ * sci_efuse_get_cal_v2 - read 4 point adc calibration data saved in efuse
+ * @pdata: adc data pointer
+ * pdata[0] -> 4.2v
+ * pdata[1] -> 3.6v
+ * pdata[3] -> 0.9v
+ * pdata[4] -> 0.3v
+ * @num: the length of adc data
+ *
+ * retruns 0 if success, else
+ * returns negative number.
+ */
+int sci_efuse_get_cal_v2(unsigned int *pdata, int num)
+{
+ int i;
+ u32 efuse_data;
+ u8 *delta = (u8 *) & efuse_data;
+ const u16 ideal[4][2] = {
+ {4200, 830}, /* FIXME: efuse only support 10bits */
+ {3600, 711},
+ {900, 737},
+ {300, 255},
+ };
+
+ efuse_data = __ddie_efuse_read(BLK_ADC_DETA_ABC);
+
+ pr_info("%s efuse block(%d) data: 0x%08x\n", __func__, BLK_ADC_DETA_ABC, efuse_data);
+
+ WARN_ON(!(efuse_data & BIT(31))); /* BIT 31 is protected bit */
+ efuse_data &= 0xFFFFFF; /* get BIT0 ~ BIT23 in block 7 */
+
+ if (!(efuse_data) || (!pdata)) {
+ return -1;
+ }
+
+ WARN_ON(!((delta[0] > delta[1]) && (delta[1] > delta[2])));
+
+ for (i = 0; i < num; i++) {
+ pdata[i] =
+ (unsigned int)ADC_VOL(delta[i], ideal[i][1], ideal[i][0]);
+ if (3 == i) {
+ efuse_data = __ddie_efuse_read(BLK_ADC_DETA_D);
+ pr_info("efuse block(%d) data: 0x%08x\n", BLK_ADC_DETA_D, efuse_data);
+ WARN_ON(!(efuse_data & BIT(31)));
+ efuse_data &= 0xFF0000; /* get BIT16 ~ BIT23 in block 9 */
+ if (!(efuse_data)) {
+ return -2;
+ }
+ pdata[i] =
+ (unsigned int)ADC_VOL(delta[2], ideal[i][1],
+ ideal[i][0]);
+ }
+ }
+
+ /* dump adc calibration parameters */
+ printk("%s adc_cal: ", __func__);
+ for (i = 0; i < num; i++) {
+ printk("%d -- %d; \n", (pdata[i] & 0xFFFF),
+ (pdata[i] >> 16) & 0xFFFF);
+ }
+ printk("\n");
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(sci_efuse_get_cal_v2);
+
+/*
+* sci_efuse_get_cal_v3 - read 2 point adc calibration data saved in efuse
+* @pdata: adc data pointer
+* pdata[0] -> 0.9v
+* pdata[1] -> 0.3v
+* @num: the length of adc data
+*
+* retruns 0 if success, else
+* returns negative number.
+*/
+int sci_efuse_get_cal_v3(unsigned int *pdata, int num)
+{
+ int i;
+ u32 efuse_data;
+ u8 *delta = (u8 *) & efuse_data;
+ const u16 ideal[2][2] = {
+ {900, 737},
+ {300, 245},
+ };
+ if (!pdata || num < 2) {
+ return -1;
+ }
+ efuse_data = __ddie_efuse_read(BLK_ADC_DETA_ABC);
+ pr_info("%s efuse block(%d) data: 0x%08x\n", __func__, BLK_ADC_DETA_ABC, efuse_data);
+ WARN_ON(!(efuse_data & BIT(31)));
+ /* BIT 31 is protected bit */
+ efuse_data &= 0xFF0000;
+ /* get BIT16 ~ BIT23 in block 7 */
+ if (!efuse_data) {
+ return -2;
+ }
+ pdata[0] = (unsigned int)ADC_VOL(delta[2], ideal[0][1], ideal[0][0]);
+ efuse_data = __ddie_efuse_read(BLK_ADC_DETA_D);
+ pr_info("efuse block(%d) data: 0x%08x\n", BLK_ADC_DETA_D, efuse_data);
+ WARN_ON(!(efuse_data & BIT(31)));
+ efuse_data &= 0xFF0000;
+ /* get BIT16 ~ BIT23 in block 9 */
+ if (!(efuse_data)) {
+ return -3;
+ }
+ pdata[1] = (unsigned int)ADC_VOL(delta[2], ideal[1][1],ideal[1][0]);
+ /* dump adc calibration parameters */
+ printk("%s adc_cal: \n", __func__);
+ for (i = 0; i < num; i++) {
+ printk("%d -- %d; \n", (pdata[i] & 0xFFFF),
+ (pdata[i] >> 16) & 0xFFFF);
+ }
+ printk("\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sci_efuse_get_cal_v3);
+
+#if defined(CONFIG_ADIE_SC2723) || defined(CONFIG_ADIE_SC2723S)
+
+int sci_efuse_ib_trim_get(unsigned int *p_cal_data)
+{
+ unsigned int data,blk0;
+
+ blk0 = __adie_efuse_read(0);
+ if (blk0 & (1 << 7)) {
+ return 0;
+ }
+
+ data = __adie_efuse_read_bits(BITSINDEX(15, 0), 7);
+ *p_cal_data = data;
+ return 1;
+}
+EXPORT_SYMBOL_GPL(sci_efuse_ib_trim_get);
+
+#endif
diff --git a/drivers/misc/sprd_otp/sprd_otp.h b/drivers/misc/sprd_otp/sprd_otp.h
new file mode 100644
index 00000000..bfd057e8
--- /dev/null
+++ b/drivers/misc/sprd_otp/sprd_otp.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications 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.
+ *
+ */
+
+#ifndef __SPRD_OTP_H__
+#define __SPRD_OTP_H__
+
+#include <linux/miscdevice.h>
+
+struct sprd_otp_operations {
+ void (*reset) (void);
+ u32(*read) (int blk_index);
+ int (*prog) (int blk_index, u32 data);
+};
+
+struct sprd_otp_device {
+ struct miscdevice misc;
+ int blk_max; /* maximun valid blocks */
+ int blk_width; /* bytes counts per block */
+ struct sprd_otp_operations *ops;
+ struct list_head list;
+};
+#define BITSINDEX(b, o) ( (b) * 8 + (o) )
+
+int sprd_efuse_init(void);
+int sprd_adie_efuse_init(void);
+u32 __adie_efuse_read(int blk_index);
+u32 __adie_efuse_read_bits(int bit_index, int length);
+
+int sprd_adie_laserfuse_init(void);
+void *sprd_otp_register(const char *name, void *ops, int blk_max,
+ int blk_width);
+
+/* EXPORT API */
+u32 __ddie_efuse_read(int blk_index);
+u32 __adie_laserfuse_read(int blk_index);
+u32 __adie_efuse_read(int blk_index);
+u32 __adie_efuse_read_bits(int bit_index, int length);
+
+#endif /* __SPRD_OTP_H__ */