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 = ®map_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 = ®map_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, ®map_ram, data, config, lock_key, lock_name); + if (IS_ERR(map)) { + kfree(data->read); + kfree(data->written); + } return map; }