From: Dhanushkalyan G <dhanushkalyan.g@microchip.com>
To: <linux-gpio@vger.kernel.org>
Cc: <linux-kernel@vger.kernel.org>, <gregkh@linuxfoundation.org>,
<arnd@arndb.de>, <vaibhaavram.tl@microchip.com>,
<kumaravel.thiagarajan@microchip.com>,
<tharunkumar.pasumarthi@microchip.com>,
<Thangaraj.S@microchip.com>
Subject: [PATCH] misc: microchip: pci1xxxx: Acquire system lock per byte for OTP/EEPROM
Date: Thu, 6 Aug 2026 16:36:47 +0530 [thread overview]
Message-ID: <20260806110647.9728-1-dhanushkalyan.g@microchip.com> (raw)
Access to the OTP and EEPROM is guarded by a hardware system lock
(CFG_SYS_LOCK register) that has a hardware timeout and is released once
the timeout elapses.
The read and write helpers acquire the lock once before the per-byte
loop and release it only after the whole buffer is transferred. As each
byte access polls the hardware for completion, a multi-byte transfer
takes longer than the lock timeout, so the lock is released mid-transfer
and the remaining bytes are not written.
Acquire and release the system lock around each byte access instead, so
every access stays within the lock timeout. Move set_sys_lock()/
release_sys_lock() inside the loop and release the lock on the error path
before bailing out, in all four helpers: OTP read/write and EEPROM
read/write.
After each access, confirm the lock is still owned before trusting the
result: under heavy load even a single byte access can exceed the lock
timeout, in which case the hardware releases the lock early. Return
-ETIMEDOUT in that case instead of silently reporting bad data.
Fixes: 0969001569e4 ("misc: microchip: pci1xxxx: Add support to read and write into PCI1XXXX OTP via NVMEM sysfs")
Signed-off-by: Dhanushkalyan G <dhanushkalyan.g@microchip.com>
---
.../misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c | 83 +++++++++++++++----
1 file changed, 65 insertions(+), 18 deletions(-)
diff --git a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
index a2ed477e0370..1e99bf59fdd3 100644
--- a/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
+++ b/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
@@ -94,6 +94,23 @@ static void release_sys_lock(struct pci1xxxx_otp_eeprom_device *priv)
writel(0, sys_lock);
}
+/*
+ * The system lock has a hardware timeout: if it is held for longer than the
+ * configured timeout, the hardware releases it automatically. Under heavy
+ * load this can happen in the middle of an OTP/EEPROM byte access, leaving
+ * the access only partially completed while the code still assumes the lock
+ * is held. Re-read the lock and confirm this function (PF3) still owns it; a
+ * cleared ownership bit means the access raced with a lock timeout and its
+ * result cannot be trusted.
+ */
+static bool is_sys_lock_owned(struct pci1xxxx_otp_eeprom_device *priv)
+{
+ void __iomem *sys_lock = priv->reg_base +
+ MMAP_CFG_OFFSET(CFG_SYS_LOCK_OFFSET);
+
+ return readl(sys_lock) & CFG_SYS_LOCK_PF3;
+}
+
static bool is_eeprom_responsive(struct pci1xxxx_otp_eeprom_device *priv)
{
void __iomem *rb = priv->reg_base;
@@ -133,11 +150,11 @@ static int pci1xxxx_eeprom_read(void *priv_t, unsigned int off,
if ((off + count) > priv->nvmem_config_eeprom.size)
count = priv->nvmem_config_eeprom.size - off;
- ret = set_sys_lock(priv);
- if (ret)
- return ret;
-
for (byte = 0; byte < count; byte++) {
+ ret = set_sys_lock(priv);
+ if (ret)
+ return ret;
+
writel(EEPROM_CMD_EPC_BUSY_BIT | (off + byte), rb +
MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
@@ -148,13 +165,20 @@ static int pci1xxxx_eeprom_read(void *priv_t, unsigned int off,
rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
if (ret < 0 || (!ret && (regval & EEPROM_CMD_EPC_TIMEOUT_BIT))) {
ret = -EIO;
+ release_sys_lock(priv);
goto error;
}
buf[byte] = readl(rb + MMAP_EEPROM_OFFSET(EEPROM_DATA_REG));
+
+ if (!is_sys_lock_owned(priv)) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ release_sys_lock(priv);
}
error:
- release_sys_lock(priv);
return ret;
}
@@ -174,11 +198,12 @@ static int pci1xxxx_eeprom_write(void *priv_t, unsigned int off,
if ((off + count) > priv->nvmem_config_eeprom.size)
count = priv->nvmem_config_eeprom.size - off;
- ret = set_sys_lock(priv);
- if (ret)
- return ret;
for (byte = 0; byte < count; byte++) {
+ ret = set_sys_lock(priv);
+ if (ret)
+ return ret;
+
writel(*(value + byte), rb + MMAP_EEPROM_OFFSET(EEPROM_DATA_REG));
regval = EEPROM_CMD_EPC_TIMEOUT_BIT | EEPROM_CMD_EPC_WRITE |
(off + byte);
@@ -193,11 +218,18 @@ static int pci1xxxx_eeprom_write(void *priv_t, unsigned int off,
rb + MMAP_EEPROM_OFFSET(EEPROM_CMD_REG));
if (ret < 0 || (!ret && (regval & EEPROM_CMD_EPC_TIMEOUT_BIT))) {
ret = -EIO;
+ release_sys_lock(priv);
+ goto error;
+ }
+
+ if (!is_sys_lock_owned(priv)) {
+ ret = -ETIMEDOUT;
goto error;
}
+
+ release_sys_lock(priv);
}
error:
- release_sys_lock(priv);
return ret;
}
@@ -229,11 +261,11 @@ static int pci1xxxx_otp_read(void *priv_t, unsigned int off,
if ((off + count) > priv->nvmem_config_otp.size)
count = priv->nvmem_config_otp.size - off;
- ret = set_sys_lock(priv);
- if (ret)
- return ret;
-
for (byte = 0; byte < count; byte++) {
+ ret = set_sys_lock(priv);
+ if (ret)
+ return ret;
+
otp_device_set_address(priv, (u16)(off + byte));
data = readl(rb + MMAP_OTP_OFFSET(OTP_FUNC_CMD_OFFSET));
writel(data | OTP_FUNC_RD_BIT,
@@ -251,13 +283,20 @@ static int pci1xxxx_otp_read(void *priv_t, unsigned int off,
data = readl(rb + MMAP_OTP_OFFSET(OTP_PASS_FAIL_OFFSET));
if (ret < 0 || data & OTP_FAIL_BIT) {
ret = -EIO;
+ release_sys_lock(priv);
goto error;
}
buf[byte] = readl(rb + MMAP_OTP_OFFSET(OTP_RD_DATA_OFFSET));
+
+ if (!is_sys_lock_owned(priv)) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ release_sys_lock(priv);
}
error:
- release_sys_lock(priv);
return ret;
}
@@ -278,11 +317,12 @@ static int pci1xxxx_otp_write(void *priv_t, unsigned int off,
if ((off + count) > priv->nvmem_config_otp.size)
count = priv->nvmem_config_otp.size - off;
- ret = set_sys_lock(priv);
- if (ret)
- return ret;
for (byte = 0; byte < count; byte++) {
+ ret = set_sys_lock(priv);
+ if (ret)
+ return ret;
+
otp_device_set_address(priv, (u16)(off + byte));
/*
@@ -309,11 +349,18 @@ static int pci1xxxx_otp_write(void *priv_t, unsigned int off,
data = readl(rb + MMAP_OTP_OFFSET(OTP_PASS_FAIL_OFFSET));
if (ret < 0 || data & OTP_FAIL_BIT) {
ret = -EIO;
+ release_sys_lock(priv);
goto error;
}
+
+ if (!is_sys_lock_owned(priv)) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ release_sys_lock(priv);
}
error:
- release_sys_lock(priv);
return ret;
}
--
2.34.1
reply other threads:[~2026-08-06 11:06 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260806110647.9728-1-dhanushkalyan.g@microchip.com \
--to=dhanushkalyan.g@microchip.com \
--cc=Thangaraj.S@microchip.com \
--cc=arnd@arndb.de \
--cc=gregkh@linuxfoundation.org \
--cc=kumaravel.thiagarajan@microchip.com \
--cc=linux-gpio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=tharunkumar.pasumarthi@microchip.com \
--cc=vaibhaavram.tl@microchip.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox