Skip to content

Commit 82f25bd

Browse files
acelanbroonie
authored andcommitted
regmap-i2c: add 16-bit width registers support
This allows to access data with 16-bit width of registers via i2c SMBus block functions. The multi-command sequence of the reading function is not safe and may read the wrong data from other address if other commands are sent in-between the SMBus commands in the read function. Read performance: 32768 bytes (33 kB, 32 KiB) copied, 11.4869 s, 2.9 kB/s Write performance(with 1-byte page): 32768 bytes (33 kB, 32 KiB) copied, 129.591 s, 0.3 kB/s The implementation is inspired by below commit https://patchwork.ozlabs.org/patch/545292/ v2: add more descriptions about the issue that maybe introduced by this commit Signed-off-by: AceLan Kao <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent 148c01d commit 82f25bd

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed

drivers/base/regmap/regmap-i2c.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,63 @@ static const struct regmap_bus regmap_i2c_smbus_i2c_block = {
246246
.max_raw_write = I2C_SMBUS_BLOCK_MAX,
247247
};
248248

249+
static int regmap_i2c_smbus_i2c_write_reg16(void *context, const void *data,
250+
size_t count)
251+
{
252+
struct device *dev = context;
253+
struct i2c_client *i2c = to_i2c_client(dev);
254+
255+
if (count < 2)
256+
return -EINVAL;
257+
258+
count--;
259+
return i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count,
260+
(u8 *)data + 1);
261+
}
262+
263+
static int regmap_i2c_smbus_i2c_read_reg16(void *context, const void *reg,
264+
size_t reg_size, void *val,
265+
size_t val_size)
266+
{
267+
struct device *dev = context;
268+
struct i2c_client *i2c = to_i2c_client(dev);
269+
int ret, count, len = val_size;
270+
271+
if (reg_size != 2)
272+
return -EINVAL;
273+
274+
ret = i2c_smbus_write_byte_data(i2c, ((u16 *)reg)[0] & 0xff,
275+
((u16 *)reg)[0] >> 8);
276+
if (ret < 0)
277+
return ret;
278+
279+
count = 0;
280+
do {
281+
/* Current Address Read */
282+
ret = i2c_smbus_read_byte(i2c);
283+
if (ret < 0)
284+
break;
285+
286+
*((u8 *)val++) = ret;
287+
count++;
288+
len--;
289+
} while (len > 0);
290+
291+
if (count == val_size)
292+
return 0;
293+
else if (ret < 0)
294+
return ret;
295+
else
296+
return -EIO;
297+
}
298+
299+
static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = {
300+
.write = regmap_i2c_smbus_i2c_write_reg16,
301+
.read = regmap_i2c_smbus_i2c_read_reg16,
302+
.max_raw_read = I2C_SMBUS_BLOCK_MAX,
303+
.max_raw_write = I2C_SMBUS_BLOCK_MAX,
304+
};
305+
249306
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
250307
const struct regmap_config *config)
251308
{
@@ -255,6 +312,10 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
255312
i2c_check_functionality(i2c->adapter,
256313
I2C_FUNC_SMBUS_I2C_BLOCK))
257314
return &regmap_i2c_smbus_i2c_block;
315+
else if (config->val_bits == 8 && config->reg_bits == 16 &&
316+
i2c_check_functionality(i2c->adapter,
317+
I2C_FUNC_SMBUS_I2C_BLOCK))
318+
return &regmap_i2c_smbus_i2c_block_reg16;
258319
else if (config->val_bits == 16 && config->reg_bits == 8 &&
259320
i2c_check_functionality(i2c->adapter,
260321
I2C_FUNC_SMBUS_WORD_DATA))

0 commit comments

Comments
 (0)