lkml.org 
[lkml]   [2018]   [Dec]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[PATCH v4.19 1/1]: nvmem: imx-ocotp: account for addressing gaps to ensure consistent read/write on i.MX SoCs

The nvmem-imx-ocotp driver does not currently handle addressing gaps between
OTP banks which can be present in i.MX System-on-Chip part numbers.

This leads to inconsistencies between the interpretation of the offset
argument in read and write operations, not only requiring specific
workarounds but also resulting in the impossibility of reading the entire OTP
bank.

Additionally this inconsistency violates the NVMEM subsystem principle of
exposing a userspace binary interface for read/write operations against a
pseudo-file.

This patch accounts for addressing gaps, depending on the specific i.MX model
being addressed.

Signed-off-by: Andrea Barisani <andrea@inversepath.com>

---
drivers/nvmem/imx-ocotp.c | 49 ++++++++++++++++++++++++++++++------
1 file changed, 41 insertions(+), 8 deletions(-)

diff -up linux-4.19.9/drivers/nvmem/imx-ocotp.c.orig linux-4.19.9/drivers/nvmem/imx-ocotp.c
--- linux-4.19.9/drivers/nvmem/imx-ocotp.c.orig 2018-12-18 10:33:26.665742455 +0100
+++ linux-4.19.9/drivers/nvmem/imx-ocotp.c 2018-12-18 10:33:32.569830061 +0100
@@ -125,6 +125,8 @@ static int imx_ocotp_read(void *context,
{
struct ocotp_priv *priv = context;
unsigned int count;
+ unsigned int base_offset;
+ const char *compat;
u32 *buf = val;
int i, ret;
u32 index;
@@ -150,11 +152,42 @@ static int imx_ocotp_read(void *context,
goto read_end;
}

+ compat = of_get_property(priv->dev->of_node, "compatible", NULL);
+
for (i = index; i < (index + count); i++) {
- *buf++ = readl(priv->base + IMX_OCOTP_OFFSET_B0W0 +
- i * IMX_OCOTP_OFFSET_PER_WORD);
+ /*
+ * On specific i.MX P/Ns there are addressing gaps between
+ * contiguous OTP banks which must be accounted for when
+ * reading OTP banks from mapped memory.
+ *
+ * This is necessary to make the imx_ocotp_read() offset
+ * interpretation consistent with imx_ocotp_write(), which
+ * does not require to account for gaps as it uses the offset
+ * value to derive the bank index and pass it directly to the
+ * OCOTP controller (rather than being used for mapped memory
+ * addressing).
+ */
+ base_offset = IMX_OCOTP_OFFSET_B0W0 + i * IMX_OCOTP_OFFSET_PER_WORD;
+
+ if (strcmp(compat, "fsl,imx6sx-ocotp")) {
+ /* 44.5.43 [IMX6SXRM]
+ * 256 bytes gap between Bank5 Word7 (21B_C6F0h, offset 0x6f0) and
+ * Bank10 Word1 (21B_CA10h, offset 0xa10)
+ */
+ if (base_offset > 0x6f0)
+ base_offset += 0x100;
+ } else if (strcmp(compat, "fsl,imx6ul-ocotp")) {
+ /* 35.5.59 [IMX6ULRM]
+ * 256 bytes gap between Bank5 Word7 (21B_C6F0h, offset 0x6f0) and
+ * Bank6 Word0 (21B_C800h, offset 0x800)
+ */
+ if (base_offset > 0x6f0)
+ base_offset += 0x100;
+ }
+
+ *buf++ = readl(priv->base + base_offset);

- /* 47.3.1.2
+ /* 47.3.1.2 [IMX6SDLRM]
* For "read locked" registers 0xBADABADA will be returned and
* HW_OCOTP_CTRL[ERROR] will be set. It must be cleared by
* software before any new write, read or reload access can be
@@ -177,7 +210,7 @@ static void imx_ocotp_set_imx6_timing(st
unsigned long strobe_read, relax, strobe_prog;
u32 timing = 0;

- /* 47.3.1.3.1
+ /* 47.3.1.3.1 [IMX6SDLRM]
* Program HW_OCOTP_TIMING[STROBE_PROG] and HW_OCOTP_TIMING[RELAX]
* fields with timing values to match the current frequency of the
* ipg_clk. OTP writes will work at maximum bus frequencies as long
@@ -245,7 +278,7 @@ static int imx_ocotp_write(void *context
/* Setup the write timing values */
priv->params->set_timing(priv);

- /* 47.3.1.3.2
+ /* 47.3.1.3.2 [IMX6SDLRM]
* Check that HW_OCOTP_CTRL[BUSY] and HW_OCOTP_CTRL[ERROR] are clear.
* Overlapped accesses are not supported by the controller. Any pending
* write or reload must be completed before a write access can be
@@ -257,7 +290,7 @@ static int imx_ocotp_write(void *context
goto write_end;
}

- /* 47.3.1.3.3
+ /* 47.3.1.3.3 [IMX6SDLRM]
* Write the requested address to HW_OCOTP_CTRL[ADDR] and program the
* unlock code into HW_OCOTP_CTRL[WR_UNLOCK]. This must be programmed
* for each write access. The lock code is documented in the register
@@ -289,7 +322,7 @@ static int imx_ocotp_write(void *context

writel(ctrl, priv->base + IMX_OCOTP_ADDR_CTRL);

- /* 47.3.1.3.4
+ /* 47.3.1.3.4 [IMX6SDLRM]
* Write the data to the HW_OCOTP_DATA register. This will automatically
* set HW_OCOTP_CTRL[BUSY] and clear HW_OCOTP_CTRL[WR_UNLOCK]. To
* protect programming same OTP bit twice, before program OCOTP will
@@ -344,7 +377,7 @@ static int imx_ocotp_write(void *context
writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA0);
}

- /* 47.4.1.4.5
+ /* 47.4.1.4.5 [IMX6SDLRM]
* Once complete, the controller will clear BUSY. A write request to a
* protected or locked region will result in no OTP access and no
* setting of HW_OCOTP_CTRL[BUSY]. In addition HW_OCOTP_CTRL[ERROR] will
@@ -362,7 +395,7 @@ static int imx_ocotp_write(void *context
goto write_end;
}

- /* 47.3.1.4
+ /* 47.3.1.4 [IMX6SDLRM]
* Write Postamble: Due to internal electrical characteristics of the
* OTP during writes, all OTP operations following a write must be
* separated by 2 us after the clearing of HW_OCOTP_CTRL_BUSY following
\
 
 \ /
  Last update: 2018-12-19 08:08    [W:1.081 / U:0.012 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site