* [PATCH v6 0/2] Add support for q50sn12072 and q54sn120a1
@ 2026-04-27 6:42 Brian Chiang
2026-04-27 6:42 ` [PATCH v6 1/2] dt-bindings: trivial: Add q50sn12072 and q54sn120a1 support Brian Chiang
2026-04-27 6:42 ` [PATCH v6 2/2] hwmon: (pmbus/q54sj108a2) Add support for q50sn12072 and q54sn120a1 Brian Chiang
0 siblings, 2 replies; 4+ messages in thread
From: Brian Chiang @ 2026-04-27 6:42 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Guenter Roeck
Cc: devicetree, linux-kernel, linux-hwmon, Jack Cheng, Brian Chiang,
Jack Cheng
The Q50SN12072 and Q54SN120A1 are high-efficiency, high-density DC-DC power
module from Delta Power Modules.
The Q50SN12072, quarter brick, single output 12V. This product provides up
to 1200 watts of output power at 38~60V. The Q50SN12072 offers peak
efficiency up to 98.3%@54Vin.
The Q54SN120A1, quarter brick, single output 12V. This product provides up
to 1300 watts of output power at 40~60V. The Q54SN120A1 offers peak
efficiency up to 98.1%@54Vin.
Add support for them to q54sj108a2 driver.
Signed-off-by: Jack Cheng <Cheng.JackHY@inventec.com>
Signed-off-by: Brian Chiang <chiang.brian@inventec.com>
Changes in v6:
- Add lock to debugfs read/write handlers
- Remove the incorrect Co-developed-by tag in cover letter patch reported by checkpatch.pl
- Link to v5: https://lore.kernel.org/r/20260422-add-support-for-q50sn12072-and-q54sn120a1-v5-0-b8fb87262868@inventec.com
Changes in v5:
- Rebase from 6.19 to 7.0-rc7 for review
- Link to v4: https://lore.kernel.org/r/20260414-add-support-for-q50sn12072-and-q54sn120a1-v4-0-b81eaea49df1@inventec.com
Changes in v4:
- Add null terminator to prevent comparison of uninitialized data which
takes place when ret is shorter than strlen(mid->name)
- Link to v3: https://lore.kernel.org/r/20260402-add-support-for-q50sn12072-and-q54sn120a1-v3-0-67a5184e93b8@inventec.com
Changes in v3:
- Fix MFR_MODEL detection by using strncasecmp prefix match, without the strict length equality
- Move blackbox_read_offset debugfs entry inside the q54sj108a2-only guard block
- Sort the debugfs entries by the order of PMBus register addresses
- Link to v2: https://lore.kernel.org/r/20260326-add-support-for-q50sn12072-and-q54sn120a1-v2-0-77bc77eedc76@inventec.com
Changes in v2:
- Drop Q50SN12072_DEBUGFS_VOUT_COMMAND debugfs entry
- Add .format[PSC_VOLTAGE_OUT] = linear explicitly to all three chip
entries for consistency
- Replace hardcoded MFR_MODEL check (ret != 14 || strncmp("Q54SJ108A2"))
with a loop over q54sj108a2_id[] using strncasecmp to support all
three chip variants dynamically
- Remove of_device_get_match_data()/i2c_match_id() early chip_id path;
derive chip_id exclusively from MFR_MODEL hardware read
- Remove unused .data fields from of_device_id entries
- Guard store_default, blackbox_erase, blackbox_set_offset, blackbox_read,
and flash_key debugfs entries under psu->chip == q54sj108a2 check
- Add dev_notice() when configured device name differs from detected model
- Update MODULE_DESCRIPTION to "PMBus driver for Delta Q54SJ108A2 and
compatibles"
- Fix commit message typo: "Q54SN12072" -> "Q50SN12072"
- Link to v1: https://lore.kernel.org/r/20250701-add-support-for-q50sn12072-and-q54sn120a1-v1-0-c387baf928cb@inventec.com
---
Jack Cheng (2):
dt-bindings: trivial: Add q50sn12072 and q54sn120a1 support
hwmon: (pmbus/q54sj108a2) Add support for q50sn12072 and q54sn120a1
.../devicetree/bindings/trivial-devices.yaml | 4 +
drivers/hwmon/pmbus/q54sj108a2.c | 179 ++++++++++++++-------
2 files changed, 123 insertions(+), 60 deletions(-)
---
base-commit: 591cd656a1bf5ea94a222af5ef2ee76df029c1d2
change-id: 20250701-add-support-for-q50sn12072-and-q54sn120a1-a9c299e6d81d
Best regards,
--
Brian Chiang <chiang.brian@inventec.com>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v6 1/2] dt-bindings: trivial: Add q50sn12072 and q54sn120a1 support
2026-04-27 6:42 [PATCH v6 0/2] Add support for q50sn12072 and q54sn120a1 Brian Chiang
@ 2026-04-27 6:42 ` Brian Chiang
2026-04-27 6:42 ` [PATCH v6 2/2] hwmon: (pmbus/q54sj108a2) Add support for q50sn12072 and q54sn120a1 Brian Chiang
1 sibling, 0 replies; 4+ messages in thread
From: Brian Chiang @ 2026-04-27 6:42 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Guenter Roeck
Cc: devicetree, linux-kernel, linux-hwmon, Jack Cheng, Brian Chiang,
Jack Cheng
From: Jack Cheng <cheng.jackhy@inventec.com>
Add support for the Delta Electronics q50sn12072 and q54sn120a1
1/4 Brick DC/DC Regulated Power Modules.
Signed-off-by: Jack Cheng <cheng.jackhy@inventec.com>
Acked-by: Rob Herring (Arm) <robh@kernel.org>
---
Documentation/devicetree/bindings/trivial-devices.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index a482aeadcd44..d4b78154df82 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -96,7 +96,11 @@ properties:
# Delta Electronics DPS920AB 920W 54V Power Supply
- delta,dps920ab
# 1/4 Brick DC/DC Regulated Power Module
+ - delta,q50sn12072
+ # 1/4 Brick DC/DC Regulated Power Module
- delta,q54sj108a2
+ # 1/4 Brick DC/DC Regulated Power Module
+ - delta,q54sn120a1
# Devantech SRF02 ultrasonic ranger in I2C mode
- devantech,srf02
# Devantech SRF08 ultrasonic ranger
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v6 2/2] hwmon: (pmbus/q54sj108a2) Add support for q50sn12072 and q54sn120a1
2026-04-27 6:42 [PATCH v6 0/2] Add support for q50sn12072 and q54sn120a1 Brian Chiang
2026-04-27 6:42 ` [PATCH v6 1/2] dt-bindings: trivial: Add q50sn12072 and q54sn120a1 support Brian Chiang
@ 2026-04-27 6:42 ` Brian Chiang
2026-04-27 7:21 ` sashiko-bot
1 sibling, 1 reply; 4+ messages in thread
From: Brian Chiang @ 2026-04-27 6:42 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Guenter Roeck
Cc: devicetree, linux-kernel, linux-hwmon, Jack Cheng, Brian Chiang,
Jack Cheng
From: Jack Cheng <cheng.jackhy@inventec.com>
The Q50SN12072 and Q54SN120A1 are high-efficiency, high-density DC-DC power
module from Delta Power Modules.
The Q50SN12072, quarter brick, single output 12V. This product provides up
to 1200 watts of output power at 38~60V. The Q50SN12072 offers peak
efficiency up to 98.3%@54Vin.
The Q54SN120A1, quarter brick, single output 12V. This product provides up
to 1300 watts of output power at 40~60V. The Q54SN120A1 offers peak
efficiency up to 98.1%@54Vin.
Add support for them to q54sj108a2 driver.
Signed-off-by: Jack Cheng <cheng.jackhy@inventec.com>
Co-developed-by: Brian Chiang <chiang.brian@inventec.com>
Signed-off-by: Brian Chiang <chiang.brian@inventec.com>
---
drivers/hwmon/pmbus/q54sj108a2.c | 179 ++++++++++++++++++++++++++-------------
1 file changed, 119 insertions(+), 60 deletions(-)
diff --git a/drivers/hwmon/pmbus/q54sj108a2.c b/drivers/hwmon/pmbus/q54sj108a2.c
index d5d60a9af8c5..b59cfee861f2 100644
--- a/drivers/hwmon/pmbus/q54sj108a2.c
+++ b/drivers/hwmon/pmbus/q54sj108a2.c
@@ -22,7 +22,9 @@
#define PMBUS_FLASH_KEY_WRITE 0xEC
enum chips {
- q54sj108a2
+ q50sn12072,
+ q54sj108a2,
+ q54sn120a1
};
enum {
@@ -55,10 +57,24 @@ struct q54sj108a2_data {
#define to_psu(x, y) container_of((x), struct q54sj108a2_data, debugfs_entries[(y)])
static struct pmbus_driver_info q54sj108a2_info[] = {
- [q54sj108a2] = {
+ [q50sn12072] = {
.pages = 1,
+ /* Source : Delta Q50SN12072 */
+ .format[PSC_VOLTAGE_OUT] = linear,
+ .format[PSC_TEMPERATURE] = linear,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_CURRENT_OUT] = linear,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN |
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_POUT,
+ },
+ [q54sj108a2] = {
+ .pages = 1,
/* Source : Delta Q54SJ108A2 */
+ .format[PSC_VOLTAGE_OUT] = linear,
.format[PSC_TEMPERATURE] = linear,
.format[PSC_VOLTAGE_IN] = linear,
.format[PSC_CURRENT_OUT] = linear,
@@ -69,6 +85,20 @@ static struct pmbus_driver_info q54sj108a2_info[] = {
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
PMBUS_HAVE_STATUS_INPUT,
},
+ [q54sn120a1] = {
+ .pages = 1,
+ /* Source : Delta Q54SN120A1 */
+ .format[PSC_VOLTAGE_OUT] = linear,
+ .format[PSC_TEMPERATURE] = linear,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_CURRENT_OUT] = linear,
+
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN |
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_POUT,
+ },
};
static ssize_t q54sj108a2_debugfs_read(struct file *file, char __user *buf,
@@ -83,73 +113,77 @@ static ssize_t q54sj108a2_debugfs_read(struct file *file, char __user *buf,
char *out = data;
char *res;
+ rc = pmbus_lock_interruptible(psu->client);
+ if (rc)
+ return rc;
+
switch (idx) {
case Q54SJ108A2_DEBUGFS_OPERATION:
rc = i2c_smbus_read_byte_data(psu->client, PMBUS_OPERATION);
if (rc < 0)
- return rc;
+ goto unlock;
rc = snprintf(data, 3, "%02x", rc);
break;
case Q54SJ108A2_DEBUGFS_WRITEPROTECT:
rc = i2c_smbus_read_byte_data(psu->client, PMBUS_WRITE_PROTECT);
if (rc < 0)
- return rc;
+ goto unlock;
rc = snprintf(data, 3, "%02x", rc);
break;
case Q54SJ108A2_DEBUGFS_VOOV_RESPONSE:
rc = i2c_smbus_read_byte_data(psu->client, PMBUS_VOUT_OV_FAULT_RESPONSE);
if (rc < 0)
- return rc;
+ goto unlock;
rc = snprintf(data, 3, "%02x", rc);
break;
case Q54SJ108A2_DEBUGFS_IOOC_RESPONSE:
rc = i2c_smbus_read_byte_data(psu->client, PMBUS_IOUT_OC_FAULT_RESPONSE);
if (rc < 0)
- return rc;
+ goto unlock;
rc = snprintf(data, 3, "%02x", rc);
break;
case Q54SJ108A2_DEBUGFS_PMBUS_VERSION:
rc = i2c_smbus_read_byte_data(psu->client, PMBUS_REVISION);
if (rc < 0)
- return rc;
+ goto unlock;
rc = snprintf(data, 3, "%02x", rc);
break;
case Q54SJ108A2_DEBUGFS_MFR_ID:
rc = i2c_smbus_read_block_data(psu->client, PMBUS_MFR_ID, data);
if (rc < 0)
- return rc;
+ goto unlock;
break;
case Q54SJ108A2_DEBUGFS_MFR_MODEL:
rc = i2c_smbus_read_block_data(psu->client, PMBUS_MFR_MODEL, data);
if (rc < 0)
- return rc;
+ goto unlock;
break;
case Q54SJ108A2_DEBUGFS_MFR_REVISION:
rc = i2c_smbus_read_block_data(psu->client, PMBUS_MFR_REVISION, data);
if (rc < 0)
- return rc;
+ goto unlock;
break;
case Q54SJ108A2_DEBUGFS_MFR_LOCATION:
rc = i2c_smbus_read_block_data(psu->client, PMBUS_MFR_LOCATION, data);
if (rc < 0)
- return rc;
+ goto unlock;
break;
case Q54SJ108A2_DEBUGFS_BLACKBOX_READ_OFFSET:
rc = i2c_smbus_read_byte_data(psu->client, READ_HISTORY_EVENT_NUMBER);
if (rc < 0)
- return rc;
+ goto unlock;
rc = snprintf(data, 3, "%02x", rc);
break;
case Q54SJ108A2_DEBUGFS_BLACKBOX_READ:
rc = i2c_smbus_read_block_data(psu->client, READ_HISTORY_EVENTS, data);
if (rc < 0)
- return rc;
+ goto unlock;
res = bin2hex(data_char, data, rc);
rc = res - data_char;
@@ -158,16 +192,22 @@ static ssize_t q54sj108a2_debugfs_read(struct file *file, char __user *buf,
case Q54SJ108A2_DEBUGFS_FLASH_KEY:
rc = i2c_smbus_read_block_data(psu->client, PMBUS_FLASH_KEY_WRITE, data);
if (rc < 0)
- return rc;
+ goto unlock;
res = bin2hex(data_char, data, rc);
rc = res - data_char;
out = data_char;
break;
default:
- return -EINVAL;
+ rc = -EINVAL;
+ goto unlock;
}
+unlock:
+ pmbus_unlock(psu->client);
+ if (rc < 0)
+ return rc;
+
out[rc] = '\n';
rc += 2;
@@ -184,25 +224,29 @@ static ssize_t q54sj108a2_debugfs_write(struct file *file, const char __user *bu
int idx = *idxp;
struct q54sj108a2_data *psu = to_psu(idxp, idx);
- rc = i2c_smbus_write_byte_data(psu->client, PMBUS_WRITE_PROTECT, 0);
+ rc = pmbus_lock_interruptible(psu->client);
if (rc)
return rc;
+ rc = i2c_smbus_write_byte_data(psu->client, PMBUS_WRITE_PROTECT, 0);
+ if (rc)
+ goto unlock;
+
switch (idx) {
case Q54SJ108A2_DEBUGFS_OPERATION:
rc = kstrtou8_from_user(buf, count, 0, &dst_data);
if (rc < 0)
- return rc;
+ goto unlock;
rc = i2c_smbus_write_byte_data(psu->client, PMBUS_OPERATION, dst_data);
if (rc < 0)
- return rc;
+ goto unlock;
break;
case Q54SJ108A2_DEBUGFS_CLEARFAULT:
rc = i2c_smbus_write_byte(psu->client, PMBUS_CLEAR_FAULTS);
if (rc < 0)
- return rc;
+ goto unlock;
break;
case Q54SJ108A2_DEBUGFS_STOREDEFAULT:
@@ -212,53 +256,59 @@ static ssize_t q54sj108a2_debugfs_write(struct file *file, const char __user *bu
flash_key[3] = 0x42;
rc = i2c_smbus_write_block_data(psu->client, PMBUS_FLASH_KEY_WRITE, 4, flash_key);
if (rc < 0)
- return rc;
+ goto unlock;
rc = i2c_smbus_write_byte(psu->client, STORE_DEFAULT_ALL);
if (rc < 0)
- return rc;
+ goto unlock;
break;
case Q54SJ108A2_DEBUGFS_VOOV_RESPONSE:
rc = kstrtou8_from_user(buf, count, 0, &dst_data);
if (rc < 0)
- return rc;
+ goto unlock;
rc = i2c_smbus_write_byte_data(psu->client, PMBUS_VOUT_OV_FAULT_RESPONSE, dst_data);
if (rc < 0)
- return rc;
+ goto unlock;
break;
case Q54SJ108A2_DEBUGFS_IOOC_RESPONSE:
rc = kstrtou8_from_user(buf, count, 0, &dst_data);
if (rc < 0)
- return rc;
+ goto unlock;
rc = i2c_smbus_write_byte_data(psu->client, PMBUS_IOUT_OC_FAULT_RESPONSE, dst_data);
if (rc < 0)
- return rc;
+ goto unlock;
break;
case Q54SJ108A2_DEBUGFS_BLACKBOX_ERASE:
rc = i2c_smbus_write_byte(psu->client, ERASE_BLACKBOX_DATA);
if (rc < 0)
- return rc;
+ goto unlock;
break;
case Q54SJ108A2_DEBUGFS_BLACKBOX_SET_OFFSET:
rc = kstrtou8_from_user(buf, count, 0, &dst_data);
if (rc < 0)
- return rc;
+ goto unlock;
rc = i2c_smbus_write_byte_data(psu->client, SET_HISTORY_EVENT_OFFSET, dst_data);
if (rc < 0)
- return rc;
+ goto unlock;
break;
default:
- return -EINVAL;
+ rc = -EINVAL;
+ goto unlock;
}
+unlock:
+ pmbus_unlock(psu->client);
+ if (rc < 0)
+ return rc;
+
return count;
}
@@ -270,7 +320,9 @@ static const struct file_operations q54sj108a2_fops = {
};
static const struct i2c_device_id q54sj108a2_id[] = {
+ { "q50sn12072", q50sn12072 },
{ "q54sj108a2", q54sj108a2 },
+ { "q54sn120a1", q54sn120a1 },
{ },
};
@@ -280,6 +332,7 @@ static int q54sj108a2_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
+ const struct i2c_device_id *mid;
enum chips chip_id;
int ret, i;
struct dentry *debugfs;
@@ -292,14 +345,9 @@ static int q54sj108a2_probe(struct i2c_client *client)
I2C_FUNC_SMBUS_BLOCK_DATA))
return -ENODEV;
- if (client->dev.of_node)
- chip_id = (enum chips)(unsigned long)of_device_get_match_data(dev);
- else
- chip_id = i2c_match_id(q54sj108a2_id, client)->driver_data;
-
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
if (ret < 0) {
- dev_err(&client->dev, "Failed to read Manufacturer ID\n");
+ dev_err(dev, "Failed to read Manufacturer ID\n");
return ret;
}
if (ret != 6 || strncmp(buf, "DELTA", 5)) {
@@ -308,19 +356,25 @@ static int q54sj108a2_probe(struct i2c_client *client)
return -ENODEV;
}
- /*
- * The chips support reading PMBUS_MFR_MODEL.
- */
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
if (ret < 0) {
dev_err(dev, "Failed to read Manufacturer Model\n");
return ret;
}
- if (ret != 14 || strncmp(buf, "Q54SJ108A2", 10)) {
- buf[ret] = '\0';
+ buf[ret] = '\0';
+ for (mid = q54sj108a2_id; mid->name[0]; mid++) {
+ if (!strncasecmp(mid->name, buf, strlen(mid->name)))
+ break;
+ }
+ if (!mid->name[0]) {
dev_err(dev, "Unsupported Manufacturer Model '%s'\n", buf);
return -ENODEV;
}
+ chip_id = mid->driver_data;
+
+ if (strcmp(client->name, mid->name) != 0)
+ dev_notice(dev, "Device mismatch: Configured %s, detected %s\n",
+ client->name, mid->name);
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_REVISION, buf);
if (ret < 0) {
@@ -341,6 +395,7 @@ static int q54sj108a2_probe(struct i2c_client *client)
if (!psu)
return 0;
+ psu->chip = chip_id;
psu->client = client;
debugfs = pmbus_get_debugfs_dir(client);
@@ -359,9 +414,6 @@ static int q54sj108a2_probe(struct i2c_client *client)
debugfs_create_file("write_protect", 0444, q54sj108a2_dir,
&psu->debugfs_entries[Q54SJ108A2_DEBUGFS_WRITEPROTECT],
&q54sj108a2_fops);
- debugfs_create_file("store_default", 0200, q54sj108a2_dir,
- &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_STOREDEFAULT],
- &q54sj108a2_fops);
debugfs_create_file("vo_ov_response", 0644, q54sj108a2_dir,
&psu->debugfs_entries[Q54SJ108A2_DEBUGFS_VOOV_RESPONSE],
&q54sj108a2_fops);
@@ -383,27 +435,34 @@ static int q54sj108a2_probe(struct i2c_client *client)
debugfs_create_file("mfr_location", 0444, q54sj108a2_dir,
&psu->debugfs_entries[Q54SJ108A2_DEBUGFS_MFR_LOCATION],
&q54sj108a2_fops);
- debugfs_create_file("blackbox_erase", 0200, q54sj108a2_dir,
- &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_ERASE],
- &q54sj108a2_fops);
- debugfs_create_file("blackbox_read_offset", 0444, q54sj108a2_dir,
- &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_READ_OFFSET],
- &q54sj108a2_fops);
- debugfs_create_file("blackbox_set_offset", 0200, q54sj108a2_dir,
- &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_SET_OFFSET],
- &q54sj108a2_fops);
- debugfs_create_file("blackbox_read", 0444, q54sj108a2_dir,
- &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_READ],
- &q54sj108a2_fops);
- debugfs_create_file("flash_key", 0444, q54sj108a2_dir,
- &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_FLASH_KEY],
- &q54sj108a2_fops);
+ if (psu->chip == q54sj108a2) {
+ debugfs_create_file("store_default", 0200, q54sj108a2_dir,
+ &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_STOREDEFAULT],
+ &q54sj108a2_fops);
+ debugfs_create_file("blackbox_erase", 0200, q54sj108a2_dir,
+ &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_ERASE],
+ &q54sj108a2_fops);
+ debugfs_create_file("blackbox_read_offset", 0444, q54sj108a2_dir,
+ &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_READ_OFFSET],
+ &q54sj108a2_fops);
+ debugfs_create_file("blackbox_read", 0444, q54sj108a2_dir,
+ &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_READ],
+ &q54sj108a2_fops);
+ debugfs_create_file("blackbox_set_offset", 0200, q54sj108a2_dir,
+ &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_SET_OFFSET],
+ &q54sj108a2_fops);
+ debugfs_create_file("flash_key", 0444, q54sj108a2_dir,
+ &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_FLASH_KEY],
+ &q54sj108a2_fops);
+ }
return 0;
}
static const struct of_device_id q54sj108a2_of_match[] = {
- { .compatible = "delta,q54sj108a2", .data = (void *)q54sj108a2 },
+ { .compatible = "delta,q50sn12072" },
+ { .compatible = "delta,q54sj108a2" },
+ { .compatible = "delta,q54sn120a1" },
{ },
};
@@ -421,6 +480,6 @@ static struct i2c_driver q54sj108a2_driver = {
module_i2c_driver(q54sj108a2_driver);
MODULE_AUTHOR("Xiao.Ma <xiao.mx.ma@deltaww.com>");
-MODULE_DESCRIPTION("PMBus driver for Delta Q54SJ108A2 series modules");
+MODULE_DESCRIPTION("PMBus driver for Delta Q54SJ108A2 and compatibles");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("PMBUS");
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v6 2/2] hwmon: (pmbus/q54sj108a2) Add support for q50sn12072 and q54sn120a1
2026-04-27 6:42 ` [PATCH v6 2/2] hwmon: (pmbus/q54sj108a2) Add support for q50sn12072 and q54sn120a1 Brian Chiang
@ 2026-04-27 7:21 ` sashiko-bot
0 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-04-27 7:21 UTC (permalink / raw)
To: Brian Chiang; +Cc: linux-hwmon
Sashiko AI review found 4 potential issue(s):
- [High] Denial of Service due to holding hardware lock while copying from user space
- [High] Use-After-Free of `psu` context due to incorrect devres initialization order
- [High] Hardware write protection is permanently disabled upon debugfs write
- [Medium] Guideline Violation: Use of incorrect lock for debugfs attributes
--
commit d199d1aceab7c176fa10e4adf36411621ba1481b
Author: Jack Cheng <cheng.jackhy@inventec.com>
hwmon: (pmbus/q54sj108a2) Add support for q50sn12072 and q54sn120a1
This commit adds support for the Q50SN12072 and Q54SN120A1 power modules
to the q54sj108a2 driver. It introduces new driver information structures,
device match tables, and debugfs entries for the supported hardware.
> diff --git a/drivers/hwmon/pmbus/q54sj108a2.c b/drivers/hwmon/pmbus/q54sj108a2.c
> index d5d60a9af8c5..b59cfee861f2 100644
> --- a/drivers/hwmon/pmbus/q54sj108a2.c
> +++ b/drivers/hwmon/pmbus/q54sj108a2.c
[ ... ]
> @@ -83,73 +113,77 @@ static ssize_t q54sj108a2_debugfs_read(struct file *file, char __user *buf,
> char *out = data;
> char *res;
>
> + rc = pmbus_lock_interruptible(psu->client);
> + if (rc)
> + return rc;
The hardware monitoring subsystem guidelines state that drivers must implement
locking for attributes registered by other means (such as debugfs) using
hwmon_lock() and hwmon_unlock().
Is pmbus_lock_interruptible() the correct lock to use here, or should this be
using the standard hwmon subsystem locks?
[ ... ]
> @@ -184,25 +224,29 @@ static ssize_t q54sj108a2_debugfs_write(struct file *file, const char __user *bu
> int idx = *idxp;
> struct q54sj108a2_data *psu = to_psu(idxp, idx);
>
> - rc = i2c_smbus_write_byte_data(psu->client, PMBUS_WRITE_PROTECT, 0);
> + rc = pmbus_lock_interruptible(psu->client);
> if (rc)
> return rc;
>
> + rc = i2c_smbus_write_byte_data(psu->client, PMBUS_WRITE_PROTECT, 0);
> + if (rc)
> + goto unlock;
This isn't introduced by this patch, but this code unconditionally disables
hardware write protection.
Does the driver ever restore the PMBUS_WRITE_PROTECT register to its
original state? It looks like the power supply module might be left
permanently unprotected against unintended I2C writes.
> switch (idx) {
> case Q54SJ108A2_DEBUGFS_OPERATION:
> rc = kstrtou8_from_user(buf, count, 0, &dst_data);
> if (rc < 0)
> - return rc;
> + goto unlock;
Could calling kstrtou8_from_user() while holding pmbus_lock_interruptible()
introduce a denial of service risk?
If a user provides a buffer managed by userfaultfd, they might be able to
indefinitely pause the page fault. Because the PMBus mutex is held, other
tasks attempting to acquire the lock for this device could hang.
Can the user input be copied and parsed into local variables before acquiring
the hardware lock?
[ ... ]
> @@ -341,6 +395,7 @@ static int q54sj108a2_probe(struct i2c_client *client)
> if (!psu)
> return 0;
>
> + psu->chip = chip_id;
> psu->client = client;
This isn't a new issue, but there appears to be an existing use-after-free
risk during device unbind.
Looking earlier in q54sj108a2_probe(), pmbus_do_probe() is called before
allocating the psu context:
ret = pmbus_do_probe(client, &q54sj108a2_info[chip_id]);
if (ret)
return ret;
psu = devm_kzalloc(&client->dev, sizeof(*psu), GFP_KERNEL);
Since devres actions execute in reverse order of addition, psu will be
freed before the PMBus core cleans up the debugfs directory.
Does this leave a window during removal where debugfs files are accessible,
but any read or write will dereference the freed psu pointer?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260427-add-support-for-q50sn12072-and-q54sn120a1-v6-0-ffa77bfa65e7@inventec.com?part=2
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-04-27 7:22 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-27 6:42 [PATCH v6 0/2] Add support for q50sn12072 and q54sn120a1 Brian Chiang
2026-04-27 6:42 ` [PATCH v6 1/2] dt-bindings: trivial: Add q50sn12072 and q54sn120a1 support Brian Chiang
2026-04-27 6:42 ` [PATCH v6 2/2] hwmon: (pmbus/q54sj108a2) Add support for q50sn12072 and q54sn120a1 Brian Chiang
2026-04-27 7:21 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox