summaryrefslogtreecommitdiffstats
path: root/drivers/nvmem
diff options
context:
space:
mode:
authorRouven Czerwinski <r.czerwinski@pengutronix.de>2019-06-11 08:51:21 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2019-06-11 10:30:12 +0200
commitb7c0ec207ef11ce756e169e8507d97a57305dc87 (patch)
treecb54044fb9099524073486a7f3c32809ed213862 /drivers/nvmem
parent18ac9ebab9cb5146ea2b0d93719c89834c95ad55 (diff)
downloadbarebox-b7c0ec207ef11ce756e169e8507d97a57305dc87.tar.gz
barebox-b7c0ec207ef11ce756e169e8507d97a57305dc87.tar.xz
nvmem: ocotp: Change TIMING calculation algorithm
Port of patch from linux-arm-kernel: | From: Bryan O'Donoghue <pure.logic@nexus-software.ie> | | nvmem: imx-ocotp: Change TIMING calculation to u-boot algorithm | | The RELAX field of the OCOTP block is turning out as a zero on i.MX8MM. | This messes up the subsequent re-load of the fuse shadow registers. | | After some discussion with people @ NXP its clear we have missed a trick | here in Linux. | | The OCOTP fuse programming time has a physical minimum 'burn time' that is | not related to the ipg_clk. | | We need to define the RELAX, STROBE_READ and STROBE_PROG fields in terms of | desired timings to allow for the burn-in to safely complete. Right now only | the RELAX field is calculated in terms of an absolute time and we are | ending up with a value of zero. | | This patch inherits the u-boot timings for the OCOTP_TIMING calculation on | the i.MX6 and i.MX8. Those timings are known to work and critically specify | values such as STROBE_PROG as a minimum timing. Signed-off-by: Rouven Czerwinski <r.czerwinski@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/nvmem')
-rw-r--r--drivers/nvmem/ocotp.c38
1 files changed, 34 insertions, 4 deletions
diff --git a/drivers/nvmem/ocotp.c b/drivers/nvmem/ocotp.c
index c21327805b..3f9f162860 100644
--- a/drivers/nvmem/ocotp.c
+++ b/drivers/nvmem/ocotp.c
@@ -69,7 +69,9 @@
/* Other definitions */
#define IMX6_OTP_DATA_ERROR_VAL 0xBADABADA
-#define DEF_RELAX 20
+#define TIMING_STROBE_PROG_US 10
+#define TIMING_STROBE_READ_NS 37
+#define TIMING_RELAX_NS 17
#define MAC_OFFSET_0 (0x22 * 4)
#define IMX6UL_MAC_OFFSET_1 (0x23 * 4)
#define MAC_OFFSET_1 (0x24 * 4)
@@ -119,11 +121,39 @@ static int imx6_ocotp_set_timing(struct ocotp_priv *priv)
u32 relax, strobe_read, strobe_prog;
u32 timing;
+ /*
+ * Note: there are minimum timings required to ensure an OTP fuse burns
+ * correctly that are independent of the ipg_clk. Those values are not
+ * formally documented anywhere however, working from the minimum
+ * timings given in u-boot we can say:
+ *
+ * - Minimum STROBE_PROG time is 10 microseconds. Intuitively 10
+ * microseconds feels about right as representative of a minimum time
+ * to physically burn out a fuse.
+ *
+ * - Minimum STROBE_READ i.e. the time to wait post OTP fuse burn before
+ * performing another read is 37 nanoseconds
+ *
+ * - Minimum RELAX timing is 17 nanoseconds. This final RELAX minimum
+ * timing is not entirely clear the documentation says "This
+ * count value specifies the time to add to all default timing
+ * parameters other than the Tpgm and Trd. It is given in number
+ * of ipg_clk periods." where Tpgm and Trd refer to STROBE_PROG
+ * and STROBE_READ respectively. What the other timing parameters
+ * are though, is not specified. Experience shows a zero RELAX
+ * value will mess up a re-load of the shadow registers post OTP
+ * burn.
+ */
clk_rate = clk_get_rate(priv->clk);
- 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;
+ relax = DIV_ROUND_UP(clk_rate * TIMING_RELAX_NS, 1000000000) - 1;
+
+ strobe_read = DIV_ROUND_UP(clk_rate * TIMING_STROBE_READ_NS,
+ 1000000000);
+ strobe_read += 2 * (relax + 1) - 1;
+ strobe_prog = DIV_ROUND_CLOSEST(clk_rate * TIMING_STROBE_PROG_US,
+ 1000000);
+ strobe_prog += 2 * (relax + 1) - 1;
timing = readl(priv->base + OCOTP_TIMING) & OCOTP_TIMING_WAIT_MASK;
timing |= BF(relax, OCOTP_TIMING_RELAX);