Linux GPIO subsystem development
 help / color / mirror / Atom feed
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 v4] misc: microchip: pci1xxxx: Acquire system lock per byte for OTP/EEPROM
Date: Thu, 6 Aug 2026 16:20:53 +0530	[thread overview]
Message-ID: <20260806105053.8801-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 10:50 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=20260806105053.8801-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