[PATCH v4 1/3] hwmon: (pmbus/adm1266) add clear_blackbox debugfs entry
From: Abdurrahman Hussain
Date: Sat May 16 2026 - 21:18:45 EST
The ADM1266 blackbox can be configured in two recording modes via
BLACKBOX_CONFIG[0]: cyclic, where the device overwrites the oldest
record once the 32-record buffer fills, and single, where it stops
recording until the buffer is cleared. Deployments that need to
preserve the full record history across multiple fault episodes
typically run in single mode and need a way to clear the buffer
after the records have been collected.
Expose a write-only debugfs file alongside sequencer_state. Writing
any data to it issues the documented clear-blackbox sub-command:
a 2-byte block-write to READ_BLACKBOX (0xDE) with payload
{0xFE, 0x00} (datasheet Rev. D).
The clear is taken under pmbus_lock because READ_BLACKBOX is also
used by adm1266_nvmem_read_blackbox() to walk records one at a
time; without the lock the clear could interleave mid-iteration
and corrupt the read sequence.
Also acquire pmbus_lock in adm1266_nvmem_read() so the memset of
data->dev_mem, the blackbox refill, and the memcpy out to userspace
run as a single critical section. The nvmem core does not serialize
concurrent reg_read calls, so two concurrent reads of the nvmem
device could otherwise race the memset against the memcpy and copy
garbage to userspace.
Signed-off-by: Abdurrahman Hussain <abdurrahman@xxxxxxxxxx>
---
drivers/hwmon/pmbus/adm1266.c | 47 +++++++++++++++++++++++++++++++++++++------
1 file changed, 41 insertions(+), 6 deletions(-)
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index d90f8f80be8e..9f4709bc85af 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -331,6 +331,39 @@ static int adm1266_state_read(struct seq_file *s, void *pdata)
return 0;
}
+/*
+ * Clearing the blackbox is required when the device is configured in
+ * single-recording mode (BLACKBOX_CONFIG[0] = 0): once the 32-record
+ * buffer is full the device stops recording until cleared.
+ *
+ * The clear is issued as a 2-byte block-write to READ_BLACKBOX with
+ * payload {0xFE, 0x00} per the datasheet. READ_BLACKBOX is also used
+ * by adm1266_nvmem_read_blackbox() to walk records one at a time;
+ * both paths run under pmbus_lock so the clear cannot interleave
+ * mid-iteration and corrupt the read sequence.
+ */
+static ssize_t adm1266_clear_blackbox_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct i2c_client *client = file->private_data;
+ u8 payload[2] = { 0xFE, 0x00 };
+ int ret;
+
+ guard(pmbus_lock)(client);
+ ret = i2c_smbus_write_block_data(client, ADM1266_READ_BLACKBOX,
+ sizeof(payload), payload);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static const struct file_operations adm1266_clear_blackbox_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = adm1266_clear_blackbox_write,
+ .llseek = noop_llseek,
+};
static void adm1266_init_debugfs(struct adm1266_data *data)
{
struct dentry *root;
@@ -343,6 +376,8 @@ static void adm1266_init_debugfs(struct adm1266_data *data)
debugfs_create_devm_seqfile(&data->client->dev, "sequencer_state", data->debugfs_dir,
adm1266_state_read);
+ debugfs_create_file("clear_blackbox", 0200, data->debugfs_dir, data->client,
+ &adm1266_clear_blackbox_fops);
}
static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff)
@@ -355,7 +390,6 @@ static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff)
ret = i2c_smbus_read_block_data(data->client, ADM1266_BLACKBOX_INFO, buf);
if (ret < 0)
return ret;
-
if (ret != 4)
return -EIO;
@@ -365,7 +399,6 @@ static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff)
ret = adm1266_pmbus_block_xfer(data, ADM1266_READ_BLACKBOX, 1, &index, read_buff);
if (ret < 0)
return ret;
-
if (ret != ADM1266_BLACKBOX_SIZE)
return -EIO;
@@ -383,6 +416,8 @@ static int adm1266_nvmem_read(void *priv, unsigned int offset, void *val, size_t
if (offset + bytes > data->nvmem_config.size)
return -EINVAL;
+ guard(pmbus_lock)(data->client);
+
if (offset == 0) {
memset(data->dev_mem, 0, data->nvmem_config.size);
@@ -470,14 +505,14 @@ static int adm1266_probe(struct i2c_client *client)
if (ret < 0)
return ret;
- ret = adm1266_config_nvmem(data);
- if (ret < 0)
- return ret;
-
ret = pmbus_do_probe(client, &data->info);
if (ret)
return ret;
+ ret = adm1266_config_nvmem(data);
+ if (ret < 0)
+ return ret;
+
adm1266_init_debugfs(data);
return 0;
--
2.53.0