[PATCH v2 03/10] regmap-i2c: fix write limit clamping for adapter quirks
From: Peng Fan
Date: Fri Nov 13 2026 - 11:15:01 EST
From: Peng Fan <peng.fan@xxxxxxx>
The adapter max_write_len quirk covers the whole write message, including
the register address, while max_raw_write is the payload limit. Calculate
the payload limit before comparing it with max_raw_write, otherwise a bus
limit that fits in the message but not in the quirk payload is left
unchanged and the transfer fails in i2c_check_for_quirks().
Also avoid installing a payload limit smaller than one value.
_regmap_raw_write() splits transfers using max_raw_write / val_bytes, so a
limit below val_bytes results in a zero chunk size and a division by zero.
If the adapter cannot fit a single value after the register address, no
raw write can succeed, so keep the existing limit.
The subtraction must not underflow when max_write_len does not exceed the
register address size, so only calculate the payload limit when the quirk
leaves room for it. Apply the same minimum-size check to the read quirk
because max_raw_read is used as a divisor too.
Fixes: 611b7eb19d0a ("regmap-i2c: Subtract reg size from max_write")
Signed-off-by: Peng Fan <peng.fan@xxxxxxx>
---
drivers/base/regmap/regmap-i2c.c | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index 8663a4ec0a3d..a4a259232980 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -8,6 +8,7 @@
#include <linux/regmap.h>
#include <linux/i2c.h>
+#include <linux/bitops.h>
#include <linux/module.h>
#include "internal.h"
@@ -353,6 +354,7 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
const struct i2c_adapter_quirks *quirks;
const struct regmap_bus *bus = NULL;
struct regmap_bus *ret_bus;
+ size_t val_bytes;
u16 max_read, max_write;
if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
@@ -393,17 +395,28 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
max_read = bus->max_raw_read;
max_write = bus->max_raw_write;
+ val_bytes = BITS_TO_BYTES(config->val_bits);
quirks = i2c->adapter->quirks;
if (quirks) {
if (quirks->max_read_len &&
+ quirks->max_read_len >= val_bytes &&
(max_read == 0 || max_read > quirks->max_read_len))
max_read = quirks->max_read_len;
- if (quirks->max_write_len &&
- (max_write == 0 || max_write > quirks->max_write_len))
- max_write = quirks->max_write_len -
- (config->reg_bits + config->pad_bits) / BITS_PER_BYTE;
+ if (quirks->max_write_len) {
+ size_t reg_reserve_size, max_payload;
+
+ reg_reserve_size = (config->reg_bits + config->pad_bits) /
+ BITS_PER_BYTE;
+ if (quirks->max_write_len > reg_reserve_size) {
+ max_payload = quirks->max_write_len -
+ reg_reserve_size;
+ if (max_payload >= val_bytes &&
+ (max_write == 0 || max_write > max_payload))
+ max_write = max_payload;
+ }
+ }
if (max_read != bus->max_raw_read ||
max_write != bus->max_raw_write) {
--
2.56.0