* Freescale On-Chip OTP driver
*
* Copyright (C) 2010-2016 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2017 NXP
*
* 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
#define BP_OCOTP_CTRL_ADDR 0
#define BM_OCOTP_CTRL_ADDR 0x0000007F
#define BM_OCOTP_CTRL_ADDR_MX7D 0x0000000F
+#define BP_OCOTP_CTRL_ADDR_MX7ULP 0
+#define BM_OCOTP_CTRL_ADDR_MX7ULP 0x000000FF
#define HW_OCOTP_TIMING 0x00000010
#define BP_OCOTP_TIMING_STROBE_READ 16
#define HW_OCOTP_DATA2_MX7D 0x00000040
#define HW_OCOTP_DATA3_MX7D 0x00000050
+#define HW_OCOTP_PDN_ULP 0x00000010
+#define HW_OCOTP_OUT_STATUS_ULP 0x00000090
+#define HW_OCOTP_OUT_STATUS_CLR_ULP 0x00000098
+
+#define BM_OUT_STATUS_DED_RELOAD_ULP (1 << 20)
+#define BM_OUT_STATUS_SEC_RELOAD_ULP (1 << 19)
+#define BM_OUT_STATUS_PROGFAIL (1 << 12)
+#define BM_OUT_STATUS_LOCKED (1 << 11)
+#define BM_OUT_STATUS_DED_ULP (1 << 10)
+
#define HW_OCOTP_CUST_N(n) (0x00000400 + (n) * 0x10)
#define BF(value, field) (((value) << BP_##field) & BM_##field)
BANK4(CRC_GP10, CRC_GP11, CRC_GP20, CRC_GP21),
};
+static const char *imx7ulp_otp_desc[][8] = {
+ BANK8(TESTER0, TESTER1, TESTER2, TESTER3, TESTER4, TESTER5, TESTER6, TESTER7),
+ BANK8(LOCK0, LOCK1, LOCK2, CFG0, CFG1, CFG2, CFG3, CFG4),
+ BANK8(BOOT0, BOOT1, BOOT2, BOOT3, BOOT4, BOOT5, BOOT6, BOOT7),
+ BANK8(MEM0, MEM1, MEM2, MEM3, ANA0, ANA1, ANA2, ANA3),
+ BANK8(OTPMK0, OTPMK1, OTPMK2, OTPMK3, OTPMK4, OTPMK5, OTPMK6, OTPMK7),
+ BANK8(A7_SRK0, A7_SRK1, A7_SRK2, A7_SRK3, A7_SRK4, A7_SRK5, A7_SRK6, A7_SRK7),
+ BANK8(M4_SRK0, M4_SRK1, M4_SRK2, M4_SRK3, M4_SRK4, M4_SRK5, M4_SRK6, M4_SRK7),
+ BANK8(SJC_RESP0, SJC_RESP1, GP0, GP1, GP2, GP3, GP4, GP5),
+ BANK8(MAU_KEY0, MAU_KEY1, MAU_KEY2, MAU_KEY3, MAU_KEY4, MAU_KEY5, MAU_KEY6, MAU_KEY7),
+ BANK8(TESTER10, TESTER11, TESTER12, TESTER13, TESTER14, TESTER15, FIELD_RETURN, SRK_REVOKE),
+ BANK8(PMU0, PMU1, PMU2, PMU3, PMU4, PMU5, PMU6, PMU7),
+ BANK8(A7_PATCH0, A7_PATCH1, A7_PATCH2, A7_PATCH3, A7_PATCH4, A7_PATCH5, A7_PATCH6, A7_PATCH7),
+ BANK8(A7_PATCH10, A7_PATCH11, A7_PATCH12, A7_PATCH13, A7_PATCH14, A7_PATCH15, A7_PATCH16, A7_PATCH17),
+ BANK8(A7_PATCH20, A7_PATCH21, A7_PATCH22, A7_PATCH23, A7_PATCH24, A7_PATCH25, A7_PATCH26, A7_PATCH27),
+ BANK8(A7_PATCH30, A7_PATCH31, A7_PATCH32, A7_PATCH33, A7_PATCH34, A7_PATCH35, A7_PATCH36, A7_PATCH37),
+ BANK8(GP10, GP11, GP12, GP13, GP14, GP15, GP16, GP17),
+ BANK8(GP20, GP21, GP22, GP23, GP24, GP25, GP26, GP27),
+ BANK8(GP30, GP31, GP32, GP33, GP34, GP35, GP36, GP37),
+ BANK8(GP40, GP41, GP42, GP43, GP44, GP45, GP46, GP47),
+ BANK8(GP50, GP51, GP52, GP53, GP54, GP55, GP56, GP57),
+ BANK8(M4_PATCH0, M4_PATCH1, M4_PATCH2, M4_PATCH3, M4_PATCH4, M4_PATCH5, M4_PATCH6, M4_PATCH7),
+ BANK8(M4_PATCH10, M4_PATCH11, M4_PATCH12, M4_PATCH13, M4_PATCH14, M4_PATCH15, M4_PATCH16, M4_PATCH17),
+ BANK8(M4_PATCH20, M4_PATCH21, M4_PATCH22, M4_PATCH23, M4_PATCH24, M4_PATCH25, M4_PATCH26, M4_PATCH27),
+ BANK8(M4_PATCH30, M4_PATCH31, M4_PATCH32, M4_PATCH33, M4_PATCH34, M4_PATCH35, M4_PATCH36, M4_PATCH37),
+ BANK8(GP60, GP61, GP62, GP63, GP64, GP65, GP66, GP67),
+ BANK8(GP70, GP71, GP72, GP73, GP74, GP75, GP76, GP77),
+ BANK8(GP80, GP81, GP82, GP83, GP84, GP85, GP86, GP87),
+ BANK8(GP90, GP91, GP92, GP93, GP94, GP95, GP96, GP97),
+ BANK8(TRIM0, TRIM1, TRIM2, TRIM3, TRIM4, TRIM5, TRIM6, TRIM7),
+ BANK8(OTFAD_KEY0, OTFAD_KEY1, OTFAD_KEY2, OTFAD_KEY3, OTFAD_CFG0, OTFAD_CFG1, OTFAD_CFG2, OTFAD_CFG3),
+ BANK8(CRC0, CRC1, CRC2, CRC3, CRC4, CRC5, CRC6, CRC7),
+};
+
static DEFINE_MUTEX(otp_mutex);
static void __iomem *otp_base;
static struct clk *otp_clk;
FSL_OTP_MX6UL,
FSL_OTP_MX6ULL,
FSL_OTP_MX7D,
+ FSL_OTP_MX7ULP,
};
struct fsl_otp_devtype_data {
u32 phy_bank;
if ((bank == 0) || (d->devtype == FSL_OTP_MX6SL) ||
- (d->devtype == FSL_OTP_MX7D))
+ (d->devtype == FSL_OTP_MX7D) || (d->devtype == FSL_OTP_MX7ULP))
phy_bank = bank;
else if ((d->devtype == FSL_OTP_MX6UL) ||
(d->devtype == FSL_OTP_MX6ULL) ||
__raw_writel(reg, otp_base + HW_OCOTP_TIMING);
}
+static void imx7ulp_set_otp_timing(void)
+{
+ /* No need to setup timing for ULP */
+}
+
static struct fsl_otp_devtype_data imx6q_data = {
.devtype = FSL_OTP_MX6Q,
.bank_desc = (const char **)imx6q_otp_desc,
.set_otp_timing = imx7_set_otp_timing,
};
+static struct fsl_otp_devtype_data imx7ulp_data = {
+ .devtype = FSL_OTP_MX7ULP,
+ .bank_desc = (const char **)imx7ulp_otp_desc,
+ .fuse_nums = 31 * 8,
+ .set_otp_timing = imx7ulp_set_otp_timing,
+};
+
static int otp_wait_busy(u32 flags)
{
int count;
if (ret)
goto out;
+ if (fsl_otp->devtype == FSL_OTP_MX7ULP) {
+ value = __raw_readl(otp_base + HW_OCOTP_OUT_STATUS_ULP);
+ if (value & BM_OUT_STATUS_DED_ULP) {
+ __raw_writel(BM_OUT_STATUS_DED_ULP, otp_base + HW_OCOTP_OUT_STATUS_CLR_ULP);
+ goto out;
+ }
+ }
+
value = __raw_readl(otp_base + HW_OCOTP_CUST_N(phy_index));
+ if (fsl_otp->devtype == FSL_OTP_MX7ULP) {
+ __raw_writel(1, otp_base + HW_OCOTP_PDN_ULP);
+ }
+
out:
mutex_unlock(&otp_mutex);
clk_disable_unprepare(otp_clk);
return 0;
}
+static int imx7ulp_otp_write_bits(int addr, u32 data, u32 magic)
+{
+ u32 c; /* for control register */
+
+ /* init the control register */
+ c = __raw_readl(otp_base + HW_OCOTP_CTRL);
+ c &= ~BM_OCOTP_CTRL_ADDR_MX7ULP;
+ c |= BF(addr, OCOTP_CTRL_ADDR_MX7ULP);
+ c |= BF(magic, OCOTP_CTRL_WR_UNLOCK);
+ __raw_writel(c, otp_base + HW_OCOTP_CTRL);
+
+ /* init the data register */
+ __raw_writel(data, otp_base + HW_OCOTP_DATA);
+ otp_wait_busy(0);
+
+ mdelay(2); /* Write Postamble */
+
+ return 0;
+}
+
static int imx7_otp_write_bits(int addr, u32 data, u32 magic)
{
u32 c; /* for control register */
const char *buf, size_t count)
{
unsigned int index = attr - otp_kattr;
+ unsigned int phy_index;
unsigned long value;
+ unsigned long tmp;
int ret;
if (!fsl_otp)
mutex_lock(&otp_mutex);
+ if (fsl_otp->devtype == FSL_OTP_MX7ULP) {
+ phy_index = fsl_otp_word_physical(fsl_otp, index);
+ fsl_otp->set_otp_timing();
+ ret = otp_wait_busy(0);
+ if (ret)
+ goto out;
+
+ tmp = __raw_readl(otp_base + HW_OCOTP_OUT_STATUS_ULP);
+ if (tmp & BM_OUT_STATUS_DED_ULP) {
+ __raw_writel(BM_OUT_STATUS_DED_ULP, otp_base + HW_OCOTP_OUT_STATUS_CLR_ULP);
+ goto out;
+ }
+
+ tmp = __raw_readl(otp_base + HW_OCOTP_CUST_N(phy_index));
+
+ __raw_writel(1, otp_base + HW_OCOTP_PDN_ULP);
+
+ if (tmp != 0) {
+ ret = -EPERM;
+ goto out;
+ }
+ }
+
fsl_otp->set_otp_timing();
ret = otp_wait_busy(0);
if (ret)
if (fsl_otp->devtype == FSL_OTP_MX7D)
imx7_otp_write_bits(index, value, 0x3e77);
+ else if (fsl_otp->devtype == FSL_OTP_MX7ULP)
+ imx7ulp_otp_write_bits(index, value, 0x3e77);
else
imx6_otp_write_bits(index, value, 0x3e77);
+ if (fsl_otp->devtype == FSL_OTP_MX7ULP) {
+ value = __raw_readl(otp_base + HW_OCOTP_OUT_STATUS_ULP);
+ if (value & (BM_OUT_STATUS_LOCKED | BM_OUT_STATUS_PROGFAIL))
+ printk("ulp prog fail\n");
+
+ otp_wait_busy(0);
+ }
+
/* Reload all the shadow registers */
__raw_writel(BM_OCOTP_CTRL_RELOAD_SHADOWS,
otp_base + HW_OCOTP_CTRL_SET);
udelay(1);
otp_wait_busy(BM_OCOTP_CTRL_RELOAD_SHADOWS);
+ if (fsl_otp->devtype == FSL_OTP_MX7ULP) {
+ __raw_writel(1, otp_base + HW_OCOTP_PDN_ULP);
+ }
+
out:
mutex_unlock(&otp_mutex);
clk_disable_unprepare(otp_clk);
- return ret ? 0 : count;
+ return ret ? ret : count;
}
static const struct of_device_id fsl_otp_dt_ids[] = {
{ .compatible = "fsl,imx6ul-ocotp", .data = (void *)&imx6ul_data, },
{ .compatible = "fsl,imx6ull-ocotp", .data = (void *)&imx6ull_data, },
{ .compatible = "fsl,imx7d-ocotp", .data = (void *)&imx7d_data, },
+ { .compatible = "fsl,imx7ulp-ocotp", .data = (void *)&imx7ulp_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_otp_dt_ids);