summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThilo Jeremias <tjeremias@de.adit-jv.com>2012-07-04 18:19:11 +0200
committerDirk Behme <dirk.behme@gmail.com>2012-11-11 11:42:28 +0100
commit28b17e9f105b2725577e7739c12615aee8267bfa (patch)
treeda4d3c39d747c8a97e48ae64a469bb666100563c
parent83b4ff9f2da07304d44634bdea0ea4bc19b93c87 (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.c14
-rw-r--r--arch/arm/include/asm/arch-mx6/imx-regs.h18
-rw-r--r--arch/arm/include/asm/arch-mx6/sys_proto.h5
-rw-r--r--common/Makefile1
-rw-r--r--common/cmd_imxotp.c159
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/imx_otp.c246
-rw-r--r--include/configs/mx6qarm2.h10
-rw-r--r--include/configs/mx6qsabrelite.h10
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