summaryrefslogtreecommitdiff
path: root/drivers/base
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-04-24 12:11:26 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-04-24 12:11:26 -0700
commit6e2d43100c906c6f3d68c45fd2a08ea4be92285c (patch)
tree34940de6fdcda1805d393a92d33fcad795c1e569 /drivers/base
parentd0fc5bf9fe9b89389287846f13cc4e462a89954d (diff)
parent7e555fcae40ab2ba91fd5cd54a5a83096414957f (diff)
Merge tag 'regmap-fix-v7.1-merge-window' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap
Pull regmap fixes from Mark Brown: "There's couple of patches here that came in since my pull request: - What is effectively a quirk for shoehorning support for a wider range of I2C regmaps on weirdly restricted SMBus controllers - One minor fix for a memory leak on in error handling in the dummy driver used by the KUnit tests" * tag 'regmap-fix-v7.1-merge-window' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap: regmap: ram: fix memory leaks in __regmap_init_ram() on error regmap-i2c: add SMBus byte/word reg16 bus for adapters lacking I2C_FUNC_I2C
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/regmap/regmap-i2c.c49
-rw-r--r--drivers/base/regmap/regmap-ram.c8
2 files changed, 56 insertions, 1 deletions
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index c9b39a02278e..31e30dfced19 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -303,6 +303,50 @@ static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = {
.max_raw_write = I2C_SMBUS_BLOCK_MAX - 2,
};
+/*
+ * SMBus byte/word reg16 support for adapters that have SMBUS_BYTE_DATA
+ * and SMBUS_WORD_DATA but lack I2C_FUNC_I2C and I2C_FUNC_SMBUS_I2C_BLOCK,
+ * such as the AMD PIIX4.
+ *
+ * READ: set 16-bit EEPROM address via write_byte_data(addr_lo, addr_hi),
+ * then sequentially read bytes via read_byte() (EEPROM auto-
+ * increments the address pointer). Same as the I2C-block reg16
+ * read path above.
+ *
+ * WRITE: encode the low address byte and data into a word transaction:
+ * write_word_data(addr_hi, (data_byte << 8) | addr_lo).
+ * Only single-byte writes are supported (one value per transaction).
+ */
+static int regmap_smbus_word_write_reg16(void *context, const void *data,
+ size_t count)
+{
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ u8 addr_hi, addr_lo, val;
+
+ /*
+ * data layout: [addr_hi, addr_lo, val0, val1, ...].
+ * Only single-byte value writes are supported; multi-byte would
+ * require raw I2C (or repeated word writes with incrementing address).
+ */
+ if (count != 3)
+ return -EINVAL;
+
+ addr_hi = ((u8 *)data)[0];
+ addr_lo = ((u8 *)data)[1];
+ val = ((u8 *)data)[2];
+
+ return i2c_smbus_write_word_data(i2c, addr_hi,
+ cpu_to_le16(((u16)val << 8) | addr_lo));
+}
+
+static const struct regmap_bus regmap_smbus_byte_word_reg16 = {
+ .write = regmap_smbus_word_write_reg16,
+ .read = regmap_i2c_smbus_i2c_read_reg16,
+ .max_raw_read = I2C_SMBUS_BLOCK_MAX - 2,
+ .max_raw_write = 1,
+};
+
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
const struct regmap_config *config)
{
@@ -321,6 +365,11 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK))
bus = &regmap_i2c_smbus_i2c_block_reg16;
+ else if (config->val_bits == 8 && config->reg_bits == 16 &&
+ i2c_check_functionality(i2c->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ bus = &regmap_smbus_byte_word_reg16;
else if (config->val_bits == 16 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_WORD_DATA))
diff --git a/drivers/base/regmap/regmap-ram.c b/drivers/base/regmap/regmap-ram.c
index 0272d53fead1..c7356b0d8c83 100644
--- a/drivers/base/regmap/regmap-ram.c
+++ b/drivers/base/regmap/regmap-ram.c
@@ -71,11 +71,17 @@ struct regmap *__regmap_init_ram(struct device *dev,
return ERR_PTR(-ENOMEM);
data->written = kzalloc_objs(bool, config->max_register + 1);
- if (!data->written)
+ if (!data->written) {
+ kfree(data->read);
return ERR_PTR(-ENOMEM);
+ }
map = __regmap_init(dev, &regmap_ram, data, config,
lock_key, lock_name);
+ if (IS_ERR(map)) {
+ kfree(data->read);
+ kfree(data->written);
+ }
return map;
}