[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