Skip to content

Commit bad4bd2

Browse files
nissampabroonie
authored andcommitted
regmap-i2c: add SMBus byte/word reg16 bus for adapters lacking I2C_FUNC_I2C
AMD PIIX4 SMBus adapters, present on AMD SP5/EPYC-based platforms (including Cisco 8000 series routers), support SMBUS_BYTE_DATA and SMBUS_WORD_DATA but lack I2C_FUNC_I2C and I2C_FUNC_SMBUS_I2C_BLOCK. When at24 (or any driver) requests a regmap with reg_bits=16 and val_bits=8 on such an adapter, regmap_get_i2c_bus() finds no matching bus and returns -ENOTSUPP. The existing regmap_i2c_smbus_i2c_block_reg16 bus type already implements 16-bit addressed reads using only write_byte_data() + read_byte() primitives, but its selection is gated on I2C_FUNC_SMBUS_I2C_BLOCK which these adapters lack. Add a new regmap_smbus_byte_word_reg16 bus that: READ: reuses regmap_i2c_smbus_i2c_read_reg16() -- sets the 16-bit address via write_byte_data(addr_lo, addr_hi), then reads bytes sequentially via read_byte() (EEPROM auto-increments). Requires only SMBUS_BYTE_DATA. WRITE: uses write_word_data(addr_hi, (data << 8) | addr_lo) to encode one data byte per SMBus WORD transaction. Requires only SMBUS_WORD_DATA. Single-byte writes only. The new bus is selected in regmap_get_i2c_bus() when reg_bits=16, val_bits=8, and the adapter has SMBUS_BYTE_DATA | SMBUS_WORD_DATA but not I2C_FUNC_I2C or SMBUS_I2C_BLOCK. The branch is placed after the existing I2C_BLOCK_reg16 check so adapters with full block support continue to use the faster path. This fixes at24 EEPROM probe failures on PIIX4: at24 3-0055: probe with driver at24 failed with error -524 No driver changes are required -- at24 already passes reg_bits=16 to devm_regmap_init_i2c(), which now succeeds. Signed-off-by: Nishanth Sampath Kumar <nissampa@cisco.com> Link: https://patch.msgid.link/20260407233927.498932-1-nissampa@cisco.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent 8ad7f3b commit bad4bd2

1 file changed

Lines changed: 49 additions & 0 deletions

File tree

drivers/base/regmap/regmap-i2c.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,50 @@ static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = {
303303
.max_raw_write = I2C_SMBUS_BLOCK_MAX - 2,
304304
};
305305

306+
/*
307+
* SMBus byte/word reg16 support for adapters that have SMBUS_BYTE_DATA
308+
* and SMBUS_WORD_DATA but lack I2C_FUNC_I2C and I2C_FUNC_SMBUS_I2C_BLOCK,
309+
* such as the AMD PIIX4.
310+
*
311+
* READ: set 16-bit EEPROM address via write_byte_data(addr_lo, addr_hi),
312+
* then sequentially read bytes via read_byte() (EEPROM auto-
313+
* increments the address pointer). Same as the I2C-block reg16
314+
* read path above.
315+
*
316+
* WRITE: encode the low address byte and data into a word transaction:
317+
* write_word_data(addr_hi, (data_byte << 8) | addr_lo).
318+
* Only single-byte writes are supported (one value per transaction).
319+
*/
320+
static int regmap_smbus_word_write_reg16(void *context, const void *data,
321+
size_t count)
322+
{
323+
struct device *dev = context;
324+
struct i2c_client *i2c = to_i2c_client(dev);
325+
u8 addr_hi, addr_lo, val;
326+
327+
/*
328+
* data layout: [addr_hi, addr_lo, val0, val1, ...].
329+
* Only single-byte value writes are supported; multi-byte would
330+
* require raw I2C (or repeated word writes with incrementing address).
331+
*/
332+
if (count != 3)
333+
return -EINVAL;
334+
335+
addr_hi = ((u8 *)data)[0];
336+
addr_lo = ((u8 *)data)[1];
337+
val = ((u8 *)data)[2];
338+
339+
return i2c_smbus_write_word_data(i2c, addr_hi,
340+
cpu_to_le16(((u16)val << 8) | addr_lo));
341+
}
342+
343+
static const struct regmap_bus regmap_smbus_byte_word_reg16 = {
344+
.write = regmap_smbus_word_write_reg16,
345+
.read = regmap_i2c_smbus_i2c_read_reg16,
346+
.max_raw_read = I2C_SMBUS_BLOCK_MAX - 2,
347+
.max_raw_write = 1,
348+
};
349+
306350
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
307351
const struct regmap_config *config)
308352
{
@@ -321,6 +365,11 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
321365
i2c_check_functionality(i2c->adapter,
322366
I2C_FUNC_SMBUS_I2C_BLOCK))
323367
bus = &regmap_i2c_smbus_i2c_block_reg16;
368+
else if (config->val_bits == 8 && config->reg_bits == 16 &&
369+
i2c_check_functionality(i2c->adapter,
370+
I2C_FUNC_SMBUS_BYTE_DATA |
371+
I2C_FUNC_SMBUS_WORD_DATA))
372+
bus = &regmap_smbus_byte_word_reg16;
324373
else if (config->val_bits == 16 && config->reg_bits == 8 &&
325374
i2c_check_functionality(i2c->adapter,
326375
I2C_FUNC_SMBUS_WORD_DATA))

0 commit comments

Comments
 (0)