Thread (21 messages) flat view 21 messages, 3 authors, 12d ago
COOLING12d

Revision v5 of 3 in this series.

Revisions (3)
  1. v3 [diff vs current]
  2. v4 [diff vs current]
  3. v5 current

[PATCH net-next v5 4/7] ptp: ocp: Track EEPROM fields independently

From: Ahmad Byagowi <hidden>
Date: 2026-08-14 23:11:20
Also in: linux-devicetree, linux-hardening, linux-i2c, linux-leds, lkml
Subsystem: networking drivers, opencompute ptp clock driver, ptp hardware clock support, the rest · Maintainers: Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Vadim Fedorenko, Richard Cochran, Linus Torvalds

The board ID and serial number can reside in separate EEPROM devices.
Requiring both reads to succeed before publishing either field makes a
missing serial EEPROM block board profile selection and hides an otherwise
valid board ID.

Serialize EEPROM discovery, read each field into temporary storage, and
publish its readiness only after a successful read. Let devlink report
whichever fields are available and make the serial-number sysfs attribute
propagate read failures.

Signed-off-by: Ahmad Byagowi <redacted>
---
 drivers/ptp/ptp_ocp.c | 129 ++++++++++++++++++++++++++++++------------
 1 file changed, 93 insertions(+), 36 deletions(-)
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 3d26ec1f7b9e..31a4c9dfe391 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -21,6 +21,7 @@
 #include <net/devlink.h>
 #include <linux/i2c.h>
 #include <linux/mtd/mtd.h>
+#include <linux/mutex.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/crc16.h>
 #include <linux/dpll.h>
@@ -343,6 +344,10 @@ struct ptp_ocp_serial_port {
 
 #define OCP_BOARD_ID_LEN		13
 #define OCP_SERIAL_LEN			6
+#define OCP_EEPROM_BOARD_ID		BIT(0)
+#define OCP_EEPROM_SERIAL		BIT(1)
+#define OCP_EEPROM_ALL			(OCP_EEPROM_BOARD_ID | \
+					 OCP_EEPROM_SERIAL)
 #define OCP_SMA_NUM			4
 #define OCP_SIGNAL_NUM			4
 #define OCP_FREQ_NUM			4
@@ -403,7 +408,10 @@ struct ptp_ocp {
 	u16			fw_version;
 	u8			board_id[OCP_BOARD_ID_LEN];
 	u8			serial[OCP_SERIAL_LEN];
-	bool			has_eeprom_data;
+	struct mutex		eeprom_lock; /* Serializes EEPROM discovery. */
+	bool			has_board_id;
+	bool			has_serial;
+	bool			eeprom_error_reported;
 	u32			pps_req_map;
 	int			flash_start;
 	u32			utc_tai_offset;
@@ -1969,21 +1977,50 @@ ptp_ocp_nvmem_device_put(struct nvmem_device **nvmemp)
 	*nvmemp = NULL;
 }
 
-static void
-ptp_ocp_read_eeprom(struct ptp_ocp *bp)
+static int
+ptp_ocp_read_eeprom(struct ptp_ocp *bp, unsigned int fields)
 {
 	const struct ptp_ocp_eeprom_map *map;
 	struct nvmem_device *nvmem;
+	u8 data[OCP_BOARD_ID_LEN];
+	const char *field_name = "data";
 	const void *tag;
-	int ret;
-
-	if (!bp->i2c_ctrl)
-		return;
+	unsigned int field;
+	int ret = 0;
 
 	tag = NULL;
 	nvmem = NULL;
+	mutex_lock(&bp->eeprom_lock);
+	fields &= OCP_EEPROM_ALL;
+	if (bp->has_board_id)
+		fields &= ~OCP_EEPROM_BOARD_ID;
+	if (bp->has_serial)
+		fields &= ~OCP_EEPROM_SERIAL;
+	if (!fields)
+		goto out;
+	if (!bp->i2c_ctrl || !bp->eeprom_map) {
+		ret = -ENODEV;
+		goto out;
+	}
 
 	for (map = bp->eeprom_map; map->len; map++) {
+		if (map->bp_offset == offsetof(struct ptp_ocp, board_id)) {
+			field = OCP_EEPROM_BOARD_ID;
+			field_name = "board ID";
+			if (bp->has_board_id)
+				continue;
+		} else if (map->bp_offset == offsetof(struct ptp_ocp, serial)) {
+			field = OCP_EEPROM_SERIAL;
+			field_name = "serial number";
+			if (bp->has_serial)
+				continue;
+		} else {
+			continue;
+		}
+
+		if (!(fields & field))
+			continue;
+
 		if (map->tag != tag) {
 			tag = map->tag;
 			ptp_ocp_nvmem_device_put(&nvmem);
@@ -1992,24 +2029,34 @@ ptp_ocp_read_eeprom(struct ptp_ocp *bp)
 			nvmem = ptp_ocp_nvmem_device_get(bp, tag);
 			if (IS_ERR(nvmem)) {
 				ret = PTR_ERR(nvmem);
-				goto fail;
+				goto out;
 			}
 		}
-		ret = nvmem_device_read(nvmem, map->off, map->len,
-					BP_MAP_ENTRY_ADDR(bp, map));
-		if (ret != map->len)
-			goto fail;
+		ret = nvmem_device_read(nvmem, map->off, map->len, data);
+		if (ret < 0)
+			goto out;
+
+		memcpy(BP_MAP_ENTRY_ADDR(bp, map), data, map->len);
+		if (field == OCP_EEPROM_BOARD_ID) {
+			/* Publish the field before marking it ready. */
+			smp_store_release(&bp->has_board_id, true);
+		} else {
+			/* Publish the field before marking it ready. */
+			smp_store_release(&bp->has_serial, true);
+		}
 	}
 
-	bp->has_eeprom_data = true;
+	ret = 0;
 
 out:
+	if (ret && ret != -EPROBE_DEFER && !bp->eeprom_error_reported) {
+		dev_err(&bp->pdev->dev, "failed to read EEPROM %s: %pe\n",
+			field_name, ERR_PTR(ret));
+		bp->eeprom_error_reported = true;
+	}
 	ptp_ocp_nvmem_device_put(&nvmem);
-	return;
-
-fail:
-	dev_err(&bp->pdev->dev, "could not read eeprom: %d\n", ret);
-	goto out;
+	mutex_unlock(&bp->eeprom_lock);
+	return ret;
 }
 
 static struct device *
@@ -2156,6 +2203,7 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
 			 struct netlink_ext_ack *extack)
 {
 	struct ptp_ocp *bp = devlink_priv(devlink);
+	const char *board_id_key = DEVLINK_INFO_VERSION_GENERIC_BOARD_ID;
 	const char *fw_image;
 	char buf[32];
 	int err;
@@ -2166,24 +2214,24 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
 	if (err)
 		return err;
 
-	if (!bp->has_eeprom_data) {
-		ptp_ocp_read_eeprom(bp);
-		if (!bp->has_eeprom_data)
-			return 0;
-	}
+	ptp_ocp_read_eeprom(bp, OCP_EEPROM_ALL);
 
-	sprintf(buf, "%pM", bp->serial);
-	err = devlink_info_serial_number_put(req, buf);
-	if (err)
-		return err;
+	/* Pairs with field publication in ptp_ocp_read_eeprom(). */
+	if (smp_load_acquire(&bp->has_serial)) {
+		sprintf(buf, "%pM", bp->serial);
+		err = devlink_info_serial_number_put(req, buf);
+		if (err)
+			return err;
+	}
 
-	snprintf(buf, sizeof(buf), "%.*s", OCP_BOARD_ID_LEN,
-		 (const char *)bp->board_id);
-	err = devlink_info_version_fixed_put(req,
-			DEVLINK_INFO_VERSION_GENERIC_BOARD_ID,
-			buf);
-	if (err)
-		return err;
+	/* Pairs with field publication in ptp_ocp_read_eeprom(). */
+	if (smp_load_acquire(&bp->has_board_id)) {
+		snprintf(buf, sizeof(buf), "%.*s", OCP_BOARD_ID_LEN,
+			 (const char *)bp->board_id);
+		err = devlink_info_version_fixed_put(req, board_id_key, buf);
+		if (err)
+			return err;
+	}
 
 	return 0;
 }
@@ -3757,9 +3805,17 @@ static ssize_t
 serialnum_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	struct ptp_ocp *bp = dev_get_drvdata(dev);
+	int ret;
 
-	if (!bp->has_eeprom_data)
-		ptp_ocp_read_eeprom(bp);
+	/* Pairs with field publication in ptp_ocp_read_eeprom(). */
+	if (!smp_load_acquire(&bp->has_serial)) {
+		ret = ptp_ocp_read_eeprom(bp, OCP_EEPROM_SERIAL);
+		if (ret)
+			return ret;
+		/* Pairs with field publication in ptp_ocp_read_eeprom(). */
+		if (!smp_load_acquire(&bp->has_serial))
+			return -ENODATA;
+	}
 
 	return sysfs_emit(buf, "%pM\n", bp->serial);
 }
@@ -4729,6 +4785,7 @@ ptp_ocp_device_init(struct ptp_ocp *bp, struct pci_dev *pdev)
 
 	bp->ptp_info = ptp_ocp_clock_info;
 	spin_lock_init(&bp->lock);
+	mutex_init(&bp->eeprom_lock);
 
 	for (i = 0; i < __PORT_COUNT; i++)
 		bp->port[i].line = -1;
-- 
2.50.1 (Apple Git-155)
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help