[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)