diff options
author | Thilo Jeremias <tjeremias@de.adit-jv.com> | 2012-07-04 18:19:11 +0200 |
---|---|---|
committer | Dirk Behme <dirk.behme@gmail.com> | 2012-11-11 11:42:28 +0100 |
commit | 28b17e9f105b2725577e7739c12615aee8267bfa (patch) | |
tree | da4d3c39d747c8a97e48ae64a469bb666100563c | |
parent | 83b4ff9f2da07304d44634bdea0ea4bc19b93c87 (diff) |
i.MX6: add OTP support
Signed-off-by: Thilo Jeremias <tjeremias@de.adit-jv.com>
Signed-off-by: Dirk Behme <dirk.behme@de.bosch.com>
-rw-r--r-- | arch/arm/cpu/armv7/mx6/clock.c | 14 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-mx6/imx-regs.h | 18 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-mx6/sys_proto.h | 5 | ||||
-rw-r--r-- | common/Makefile | 1 | ||||
-rw-r--r-- | common/cmd_imxotp.c | 159 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/imx_otp.c | 246 | ||||
-rw-r--r-- | include/configs/mx6qarm2.h | 10 | ||||
-rw-r--r-- | include/configs/mx6qsabrelite.h | 10 |
9 files changed, 464 insertions, 0 deletions
diff --git a/arch/arm/cpu/armv7/mx6/clock.c b/arch/arm/cpu/armv7/mx6/clock.c index a01d96f48e..abbe8c8368 100644 --- a/arch/arm/cpu/armv7/mx6/clock.c +++ b/arch/arm/cpu/armv7/mx6/clock.c @@ -49,6 +49,20 @@ void enable_usboh3_clk(unsigned char enable) __raw_writel(reg, &imx_ccm->CCGR6); } +#ifdef CONFIG_IMX_OTP +void enable_otp_clk(unsigned char enable) +{ + u32 reg; + + reg = __raw_readl(&imx_ccm->CCGR2); + if (enable) + reg |= MXC_CCM_CCGR2_OCOTP_CTRL_MASK; + else + reg &= ~(MXC_CCM_CCGR2_OCOTP_CTRL_MASK); + __raw_writel(reg, &imx_ccm->CCGR2); + +} +#endif #ifdef CONFIG_I2C_MXC /* i2c_num can be from 0 - 2 */ diff --git a/arch/arm/include/asm/arch-mx6/imx-regs.h b/arch/arm/include/asm/arch-mx6/imx-regs.h index 3eb0081ca8..93d1901f45 100644 --- a/arch/arm/include/asm/arch-mx6/imx-regs.h +++ b/arch/arm/include/asm/arch-mx6/imx-regs.h @@ -601,5 +601,23 @@ struct iomuxc_base_regs { u32 daisy[104]; /* 0x7b0..94c */ }; +#define BP_OCOTP_CTRL_WR_UNLOCK 16 +#define BM_OCOTP_CTRL_WR_UNLOCK 0xFFFF0000 +#define BV_OCOTP_CTRL_WR_UNLOCK__KEY 0x3E77 +#define BM_OCOTP_CTRL_RELOAD_SHADOWS 0x00000400 +#define BM_OCOTP_CTRL_ERROR 0x00000200 +#define BM_OCOTP_CTRL_BUSY 0x00000100 +#define BP_OCOTP_CTRL_ADDR 0 +#define BM_OCOTP_CTRL_ADDR 0x0000007F + +#define BP_OCOTP_TIMING_STROBE_READ 16 +#define BM_OCOTP_TIMING_STROBE_READ 0x003F0000 +#define BP_OCOTP_TIMING_RELAX 12 +#define BM_OCOTP_TIMING_RELAX 0x0000F000 +#define BP_OCOTP_TIMING_STROBE_PROG 0 +#define BM_OCOTP_TIMING_STROBE_PROG 0x00000FFF + +#define BM_OCOTP_READ_CTRL_READ_FUSE 0x00000001 + #endif /* __ASSEMBLER__*/ #endif /* __ASM_ARCH_MX6_IMX_REGS_H__ */ diff --git a/arch/arm/include/asm/arch-mx6/sys_proto.h b/arch/arm/include/asm/arch-mx6/sys_proto.h index 3193297610..a3478d4e16 100644 --- a/arch/arm/include/asm/arch-mx6/sys_proto.h +++ b/arch/arm/include/asm/arch-mx6/sys_proto.h @@ -46,4 +46,9 @@ void set_vddsoc(u32 mv); int fecmxc_initialize(bd_t *bis); u32 get_ahb_clk(void); u32 get_periph_clk(void); + +/* OTP related functionality */ +int imx_otp_read_one_u32(u32, u32 *); +int imx_otp_blow_one_u32(u32, u32, u32 *); +void enable_otp_clk(unsigned char); #endif diff --git a/common/Makefile b/common/Makefile index 9e43322211..d05e3572a9 100644 --- a/common/Makefile +++ b/common/Makefile @@ -104,6 +104,7 @@ COBJS-$(CONFIG_CMD_GPIO) += cmd_gpio.o COBJS-$(CONFIG_CMD_I2C) += cmd_i2c.o COBJS-$(CONFIG_CMD_IDE) += cmd_ide.o COBJS-$(CONFIG_CMD_IMMAP) += cmd_immap.o +COBJS-$(CONFIG_CMD_IMXOTP) += cmd_imxotp.o COBJS-$(CONFIG_CMD_INI) += cmd_ini.o COBJS-$(CONFIG_CMD_IRQ) += cmd_irq.o COBJS-$(CONFIG_CMD_ITEST) += cmd_itest.o diff --git a/common/cmd_imxotp.c b/common/cmd_imxotp.c new file mode 100644 index 0000000000..3d5b98a61f --- /dev/null +++ b/common/cmd_imxotp.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <config.h> +#include <common.h> +#include <command.h> +#include <asm/arch/sys_proto.h> + +/* meant to reject most of the zeros return by simple_strtoul call */ +int validate_zero_from_simple_strtoul(char *str) +{ + int i; + + /* reject negatives */ + if (str[0] == '-') + return -1; + + /* reject no zero initialed */ + if (str[0] != '0') + return -1; + + /* accept zero */ + if (strlen(str) == 1) + return 0; + + /* only accept started with "0x" or "0X" or "00" */ + if (str[1] != 'x' && str[1] != 'X' && str[1] != '0') + return -1; + + /* reject "0x" or "0X" or "00" only */ + if (strlen(str) == 3) + return -1; + + /* only accept all zeros */ + for (i = 1; i < strlen(str) - 1; i++) { + if (str[1] != '0') + return -1; + } + return 0; +} + +int do_imxotp(cmd_tbl_t *cmd_tbl, int flag, int argc, char * const argv[]) +{ + u32 addr, value_read; +#ifdef IMX_OTPWRITE_ENABLED + u32 fused_value, value_to_fuse; + int force_to_fuse = 0; +#endif + + if (argc < 3) { +usage: + cmd_usage(cmd_tbl); + return 1; + } + + if (!strcmp(argv[1], "read")) { + switch (argc) { + case 3: + addr = simple_strtoul(argv[2], NULL, 16); + if ((addr == 0) && + validate_zero_from_simple_strtoul(argv[2])) + goto usage; + break; + default: + goto usage; + } + printf("Reading fuse at index 0x%X\n", addr); + if (imx_otp_read_one_u32(addr, &value_read)) + return -1; + printf("Fuse at index 0x%X has the value: 0x%08X\n", addr, value_read); +#ifdef IMX_OTPWRITE_ENABLED + } else if (!strcmp(argv[1], "blow")) { + if (argc < 4 || argc > 5) + goto usage; + + if (!strcmp(argv[2], "--force")) { + force_to_fuse = 1; + addr = simple_strtoul(argv[3], NULL, 16); + if ((addr == 0) + && validate_zero_from_simple_strtoul(argv[3])) + goto usage; + value_to_fuse = simple_strtoul(argv[4], NULL, 16); + if ((value_to_fuse == 0) + && validate_zero_from_simple_strtoul(argv[4])) + goto usage; + } else { + addr = simple_strtoul(argv[2], NULL, 16); + if ((addr == 0) + && validate_zero_from_simple_strtoul(argv[2])) + goto usage; + value_to_fuse = simple_strtoul(argv[3], NULL, 16); + if ((value_to_fuse == 0) + && validate_zero_from_simple_strtoul(argv[3])) + goto usage; + } + + /* show the current value of specified address (fuse index) */ + if (imx_otp_read_one_u32(addr, &value_read)) + return -1; + printf("Current fuse at index 0x%X has the value: 0x%08X\n", + addr, value_read); + + if ((value_to_fuse & value_read) == value_to_fuse) + printf("!! Fuse blow skipped:" + " the bits have been already set.\n"); + else if (force_to_fuse) { + printf("Blowing fuse at index 0x%X, value: 0x%08X\n", + addr, value_to_fuse); + if (imx_otp_blow_one_u32(addr, + value_to_fuse, &fused_value)) { + printf("ERROR: failed to blow fuse" + " at 0x%X with value of 0x%08X\n", + addr, value_to_fuse); + } else { + printf("Operation %s fuse" + " at index 0x%X value: 0x%08X\n", + ((fused_value & value_to_fuse) == + value_to_fuse) ? "succeeded" : "failed", + addr, fused_value); + } + } else { + printf("!! Fuse blow aborted." + " if you do want to blow it." + " Please use the command:\n" + "%s blow --force %X %X\n", + argv[0], addr, value_to_fuse); + } +#endif + } else + goto usage; + + return 0; +} + +U_BOOT_CMD(imxotp, 5, 0, do_imxotp, + "One-Time Programable sub-system for i.MX processors", + "read <addr>\n" + " - read fuse at 'addr'\n" +#ifdef IMX_OTPWRITE_ENABLED + "imxotp blow [--force] <addr> <value>\n" + " - blow fuse at 'addr' with hex value 'value'\n" +#endif +); diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 271463cf14..b9cc103cd1 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -29,6 +29,7 @@ COBJS-$(CONFIG_ALI152X) += ali512x.o COBJS-$(CONFIG_DS4510) += ds4510.o COBJS-$(CONFIG_FSL_LAW) += fsl_law.o COBJS-$(CONFIG_GPIO_LED) += gpio_led.o +COBJS-$(CONFIG_IMX_OTP) += imx_otp.o COBJS-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o COBJS-$(CONFIG_NS87308) += ns87308.o COBJS-$(CONFIG_PDSP188x) += pdsp188x.o diff --git a/drivers/misc/imx_otp.c b/drivers/misc/imx_otp.c new file mode 100644 index 0000000000..b4d129938e --- /dev/null +++ b/drivers/misc/imx_otp.c @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> + +#define BF(value, field) (((value) << BP_##field) & BM_##field) +#define DEF_RELAX 20 + +#ifdef CONFIG_IMX_OTP_DEBUG +#define log(a, ...) printf("[%s,%3d]:"a"\n", __func__, __LINE__, ## __VA_ARGS__) +#else +#define log(a, ...) +#endif + +static int otp_wait_busy(u32 flags) +{ + int count; + u32 c; + struct iim_regs *otp = (struct iim_regs *)IMX_OTP_BASE; + + for (count = 10000; count >= 0; count--) { + c = readl(&otp->ctrl); + if (!(c & (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR | flags))) + break; + } + + if (count < 0) { + printf("ERROR: otp_wait_busy timeout. 0x%X\n", c); + /* clear ERROR bit, busy bit will be cleared by controller */ + writel(BM_OCOTP_CTRL_ERROR, &otp->ctrl_clr); + return -1; + } + + log("wait busy successful."); + return 0; +} + +static int set_otp_timing(void) +{ + u32 clk_rate; + u32 relax, strobe_read, strobe_prog; + u32 timing; + struct iim_regs *otp = (struct iim_regs *)IMX_OTP_BASE; + + /* get clock */ + clk_rate = mxc_get_clock(MXC_IPG_CLK); + if (clk_rate == -1) { + printf("ERROR: mxc_get_clock failed\n"); + return -1; + } + + log("clk_rate: %d.", clk_rate); + + relax = clk_rate / (1000000000 / DEF_RELAX) - 1; + strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1; + strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1; + + timing = BF(relax, OCOTP_TIMING_RELAX); + timing |= BF(strobe_read, OCOTP_TIMING_STROBE_READ); + timing |= BF(strobe_prog, OCOTP_TIMING_STROBE_PROG); + log("timing: 0x%X", timing); + + writel(timing, &otp->timing); + + return 0; +} + +static int otp_read_prep(void) +{ + return (!set_otp_timing()) ? otp_wait_busy(0) : -1; +} + +#ifdef IMX_OTPWRITE_ENABLED +static int otp_blow_prep(void) +{ + return (!set_otp_timing()) ? otp_wait_busy(0) : -1; +} + +static int otp_blow_post(void) +{ + struct iim_regs *otp = (struct iim_regs *)IMX_OTP_BASE; + + printf("Reloading shadow registers...\n"); + /* reload all the shadow registers */ + writel(BM_OCOTP_CTRL_RELOAD_SHADOWS, &otp->ctrl_set); + udelay(1); + + return otp_wait_busy(BM_OCOTP_CTRL_RELOAD_SHADOWS); +} +#endif + +static int fuse_read_addr(u32 addr, u32 *pdata) +{ + u32 ctrl_reg; + struct iim_regs *otp = (struct iim_regs *)IMX_OTP_BASE; + + ctrl_reg = readl(&otp->ctrl); + ctrl_reg &= ~BM_OCOTP_CTRL_ADDR; + ctrl_reg &= ~BM_OCOTP_CTRL_WR_UNLOCK; + ctrl_reg |= BF(addr, OCOTP_CTRL_ADDR); + writel(ctrl_reg, &otp->ctrl); + + writel(BM_OCOTP_READ_CTRL_READ_FUSE, &otp->read_ctrl); + if (otp_wait_busy(0)) + return -1; + + *pdata = readl(&otp->fuse_data); + return 0; +} + +#ifdef IMX_OTPWRITE_ENABLED +static int fuse_blow_addr(u32 addr, u32 value) +{ + u32 ctrl_reg; + struct iim_regs *otp = (struct iim_regs *)IMX_OTP_BASE; + + log("blowing..."); + + /* control register */ + ctrl_reg = readl(&otp->ctrl); + ctrl_reg &= ~BM_OCOTP_CTRL_ADDR; + ctrl_reg |= BF(addr, OCOTP_CTRL_ADDR); + ctrl_reg |= BF(BV_OCOTP_CTRL_WR_UNLOCK__KEY, OCOTP_CTRL_WR_UNLOCK); + writel(ctrl_reg, &otp->ctrl); + + writel(value, &otp->data); + if (otp_wait_busy(0)) + return -1; + + /* write postamble */ + udelay(2000); + return 0; +} +#endif + +/* + * read one u32 to indexed fuse + */ +int imx_otp_read_one_u32(u32 index, u32 *pdata) +{ + u32 ctrl_reg; + int ret = 0; + struct iim_regs *otp = (struct iim_regs *)IMX_OTP_BASE; + + log("index: 0x%X", index); + + if (index > IMX_OTP_ADDR_MAX) { + printf("ERROR: invalid address.\n"); + ret = -1; + goto exit_nop; + } + + enable_otp_clk(1); + + if (otp_read_prep()) { + ret = -1; + printf("ERROR: read preparation failed\n"); + goto exit_cleanup; + } + + if (fuse_read_addr(index, pdata)) { + ret = -1; + printf("ERROR: read failed\n"); + goto exit_cleanup; + } + + if (*pdata == IMX_OTP_DATA_ERROR_VAL) { + ctrl_reg = readl(&otp->ctrl); + if (ctrl_reg & BM_OCOTP_CTRL_ERROR) { + printf("ERROR: read fuse failed\n"); + ret = -1; + } + } + +exit_cleanup: + enable_otp_clk(0); +exit_nop: + return ret; +} + +#ifdef IMX_OTPWRITE_ENABLED +/* + * blow one u32 to indexed fuse + */ +int imx_otp_blow_one_u32(u32 index, u32 data, u32 *pfused_value) +{ + int ret = 0; + struct iim_regs *otp = (struct iim_regs *)IMX_OTP_BASE; + + enable_otp_clk(1); + + if (otp_blow_prep()) { + ret = -1; + printf("ERROR: blow preparation failed\n"); + goto exit_cleanup; + } + + if (fuse_blow_addr(index, data)) { + ret = -1; + printf("ERROR: blow fuse failed\n"); + goto exit_cleanup; + } + + if (otp_blow_post()) { + ret = -1; + printf("ERROR: blow post operation failed\n"); + goto exit_cleanup; + } + + if (readl(&otp->ctrl) & BM_OCOTP_CTRL_ERROR) { + ret = -1; + printf("ERROR: OTP control\n"); + goto exit_cleanup; + } + + if (imx_otp_read_one_u32(index, pfused_value)) { + ret = -1; + printf("ERROR: OTP read\n"); + } + +exit_cleanup: + enable_otp_clk(0); + + return ret; +} +#endif diff --git a/include/configs/mx6qarm2.h b/include/configs/mx6qarm2.h index 6fb308aaa8..5a6b9ea6a1 100644 --- a/include/configs/mx6qarm2.h +++ b/include/configs/mx6qarm2.h @@ -42,6 +42,16 @@ #define CONFIG_MXC_UART #define CONFIG_MXC_UART_BASE UART4_BASE +/* OCOTP Configs */ +#define CONFIG_CMD_IMXOTP +#ifdef CONFIG_CMD_IMXOTP +#define CONFIG_IMX_OTP +#define IMX_OTP_BASE OCOTP_BASE_ADDR +#define IMX_OTP_ADDR_MAX 0x7F +#define IMX_OTP_DATA_ERROR_VAL 0xBADABADA +//#define IMX_OTPWRITE_ENABLED +#endif + /* MMC Configs */ #define CONFIG_FSL_ESDHC #define CONFIG_FSL_USDHC diff --git a/include/configs/mx6qsabrelite.h b/include/configs/mx6qsabrelite.h index a0009e60dc..785cf48b79 100644 --- a/include/configs/mx6qsabrelite.h +++ b/include/configs/mx6qsabrelite.h @@ -64,6 +64,16 @@ #define CONFIG_I2C_MXC #define CONFIG_SYS_I2C_SPEED 100000 +/* OCOTP Configs */ +#define CONFIG_CMD_IMXOTP +#ifdef CONFIG_CMD_IMXOTP +#define CONFIG_IMX_OTP +#define IMX_OTP_BASE OCOTP_BASE_ADDR +#define IMX_OTP_ADDR_MAX 0x7F +#define IMX_OTP_DATA_ERROR_VAL 0xBADABADA +//#define IMX_OTPWRITE_ENABLED +#endif + /* MMC Configs */ #define CONFIG_FSL_ESDHC #define CONFIG_FSL_USDHC |