MLK-13538-2 char: otp: support i.MX7ULP1
authorPeng Fan <peng.fan@nxp.com>
Wed, 18 Jan 2017 02:56:10 +0000 (10:56 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:58:22 +0000 (14:58 -0500)
Add ULP1 OTP support.

No timing required for ULP1 OTP.
The CTRL_ADDR is 8 bits width.
When finished access to OTP, gate the power to OTP memory to save power.

Fix store, when invalid args, not return 0, but return the error values.
To ULP, fuse only support being programmed once, so add a check before
program.

Test log:
root@imx6qdlsolo:/sys/fsl_otp# cat HW_OCOTP_GP84
0x0
root@imx6qdlsolo:/sys/fsl_otp# echo 1 > HW_OCOTP_GP84
root@imx6qdlsolo:/sys/fsl_otp# cat HW_OCOTP_GP84
0x1
root@imx6qdlsolo:/sys/fsl_otp# echo 1 > HW_OCOTP_GP84
-sh: echo: write error: Operation not permitted
root@imx6qdlsolo:/sys/fsl_otp# echo fg > HW_OCOTP_GP84
-sh: echo: write error: Invalid argument

Signed-off-by: Peng Fan <peng.fan@nxp.com>
drivers/char/fsl_otp.c

index 3e5fc8c..2c0aa4e 100644 (file)
@@ -2,6 +2,7 @@
  * 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
@@ -32,6 +33,8 @@
 #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)
 
@@ -159,6 +172,40 @@ static const char *imx7d_otp_desc[][4] = {
        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;
@@ -175,6 +222,7 @@ enum fsl_otp_devtype {
        FSL_OTP_MX6UL,
        FSL_OTP_MX6ULL,
        FSL_OTP_MX7D,
+       FSL_OTP_MX7ULP,
 };
 
 struct fsl_otp_devtype_data {
@@ -218,7 +266,7 @@ static u32 fsl_otp_bank_physical(struct fsl_otp_devtype_data *d, int bank)
        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) ||
@@ -296,6 +344,11 @@ static void imx7_set_otp_timing(void)
        __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,
@@ -340,6 +393,13 @@ static struct fsl_otp_devtype_data imx7d_data = {
        .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;
@@ -381,8 +441,20 @@ static ssize_t fsl_otp_show(struct kobject *kobj, struct kobj_attribute *attr,
        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);
@@ -409,6 +481,26 @@ static int imx6_otp_write_bits(int addr, u32 data, u32 magic)
        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 */
@@ -461,7 +553,9 @@ static ssize_t fsl_otp_store(struct kobject *kobj, struct kobj_attribute *attr,
                             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)
@@ -477,6 +571,29 @@ static ssize_t fsl_otp_store(struct kobject *kobj, struct kobj_attribute *attr,
 
        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)
@@ -484,19 +601,33 @@ static ssize_t fsl_otp_store(struct kobject *kobj, struct kobj_attribute *attr,
 
        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[] = {
@@ -506,6 +637,7 @@ 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);