diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/misc/sprd_otp/efuse.c | |
Diffstat (limited to 'drivers/misc/sprd_otp/efuse.c')
| -rw-r--r-- | drivers/misc/sprd_otp/efuse.c | 352 |
1 files changed, 352 insertions, 0 deletions
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; +} |
