[PATCH 1/5] hwmon: (pmbus/core) Introduce pmbus_read_smbus_i2c_block_data()
Guenter Roeck <[email protected]> Mon, 3 Aug 2026 12:35:31 -0700
| Newsgroups | org.kernel.vger.linux-hwmon |
|---|---|
| Message-ID | <[email protected]> |
PMBus drivers need support for reading SMBus block data. Unfortunately, not all i2C controllers support this command. Implement pmbus_read_smbus_i2c_block_data() which first tries to use i2c_smbus_read_block_data(). If not supported, try to emulate it by calling i2c_smbus_read_i2c_block_data(). Export the new function for use in PMBus drivers. Cc: Alexis Czezar Torreno <[email protected]> Cc: Nuno Sá <[email protected]> Signed-off-by: Guenter Roeck <[email protected]> --- drivers/hwmon/pmbus/pmbus.h | 1 + drivers/hwmon/pmbus/pmbus_core.c | 102 ++++++++++++++++++++++++++++--- 2 files changed, 95 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 5fe2c415eada..2cd3216b3cd9 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -561,6 +561,7 @@ void pmbus_set_update(struct i2c_client *client, u8 reg, bool update); void pmbus_wait(struct i2c_client *client); void pmbus_update_ts(struct i2c_client *client, int op); int pmbus_set_page(struct i2c_client *client, int page, int phase); +int pmbus_read_smbus_i2c_block_data(struct i2c_client *client, u8 reg, char *data_buf); int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg); int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 6991c7f8d125..1e09abbc05d3 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -182,7 +182,12 @@ EXPORT_SYMBOL_NS_GPL(pmbus_set_update, "PMBUS"); void pmbus_wait(struct i2c_client *client) { struct pmbus_data *data = i2c_get_clientdata(client); - s64 delay = ktime_us_delta(data->next_access_backoff, ktime_get()); + s64 delay; + + if (!data) + return; + + delay = ktime_us_delta(data->next_access_backoff, ktime_get()); if (delay > 0) fsleep(delay); @@ -193,8 +198,14 @@ EXPORT_SYMBOL_NS_GPL(pmbus_wait, "PMBUS"); void pmbus_update_ts(struct i2c_client *client, int op) { struct pmbus_data *data = i2c_get_clientdata(client); - const struct pmbus_driver_info *info = data->info; - int delay = info->access_delay; + const struct pmbus_driver_info *info; + int delay; + + if (!data) + return; + + info = data->info; + delay = info->access_delay; if (op & PMBUS_OP_WRITE) delay = max(delay, info->write_delay); @@ -518,6 +529,85 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, } EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, "PMBUS"); +/** + * pmbus_read_smbus_i2c_block_data() - Read SMBus/I2C block data + * @client: Handle to slave device + * @reg: Byte interpreted by slave + * @data_buf: Byte array into which data will be read + * Return: Negative errno or number of bytes read + * + * PMBus internal function to read a SMBus block from a PMBus chip. + * + * PMBus chips report various properties using SMBus block read operations. + * However, not all I2C controllers support this operation. + * + * Execute SMBus block read if supported. If not supported, but SMBus I2C block + * read is supported, use it instead. Note that at most 31 data bytes can be + * read from the device if i2c_smbus_read_i2c_block_data() is used to read the + * data. This is a SMBUs protocol limit which can not be avoided. + * + * Return -EOPNOTSUPP if neither I2C_FUNC_SMBUS_READ_BLOCK_DATA nor + * I2C_FUNC_SMBUS_READ_I2C_BLOCK is supported. + * + * Callers must hold pmbus_lock or execute calls from the probe function. + */ +int pmbus_read_smbus_i2c_block_data(struct i2c_client *client, u8 reg, char *data_buf) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int blen, len, ret; + + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { + pmbus_wait(client); + ret = i2c_smbus_read_block_data(client, reg, data_buf); + pmbus_update_ts(client, 0); + return ret; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + dev_err_once(&client->dev, "I2C adapter does not support I2C_FUNC_SMBUS_READ_I2C_BLOCK\n"); + return -EOPNOTSUPP; + } + + /* + * If the returned data is valid SMBus block data, the first byte + * must be the data length. + * + * i2c_smbus_read_i2c_block_data() may return an error if the chip + * sends NACK before the number of requested bytes is received. + * Handle this by reading the data length first, then reading the + * entire message up to I2C_SMBUS_BLOCK_MAX bytes. This ensures + * that requested number of bytes never exceeds the number of + * bytes sent by the chip. + */ + pmbus_wait(client); + ret = i2c_smbus_read_i2c_block_data(client, reg, 1, buf); + pmbus_update_ts(client, 0); + if (ret < 0) + return ret; + if (ret != 1) + return -EIO; + + len = buf[0]; + if (len == 0) + return 0; + blen = len; + if (len >= I2C_SMBUS_BLOCK_MAX) + len = I2C_SMBUS_BLOCK_MAX - 1; + pmbus_wait(client); + ret = i2c_smbus_read_i2c_block_data(client, reg, len + 1, buf); + pmbus_update_ts(client, 0); + if (ret < 0) + return ret; + if (ret != len + 1) + return -EIO; + if (buf[0] != blen) + return -EIO; + memcpy(data_buf, buf + 1, len); + return len; +} +EXPORT_SYMBOL_NS_GPL(pmbus_read_smbus_i2c_block_data, "PMBUS"); + static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, char *data_buf) { @@ -527,11 +617,7 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, if (rv < 0) return rv; - pmbus_wait(client); - rv = i2c_smbus_read_block_data(client, reg, data_buf); - pmbus_update_ts(client, 0); - - return rv; + return pmbus_read_smbus_i2c_block_data(client, reg, data_buf); } static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page, -- 2.45.2