Add regfield support to get comfortable access to register fields in a regmap. The functions are taken directly from Linux-6.10. Signed-off-by: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> --- drivers/base/regmap/internal.h | 11 ++++ drivers/base/regmap/regmap.c | 131 +++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 39 ++++++++++++ 3 files changed, 181 insertions(+) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index ac3f0d3c0f..6f6a34edc7 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -41,6 +41,17 @@ struct regmap { unsigned int val); }; +struct regmap_field { + struct regmap *regmap; + unsigned int mask; + /* lsb */ + unsigned int shift; + unsigned int reg; + + unsigned int id_size; + unsigned int id_offset; +}; + enum regmap_endian regmap_get_val_endian(struct device *dev, const struct regmap_bus *bus, const struct regmap_config *config); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7ad527954c..1f10424a42 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -242,6 +242,30 @@ int regmap_write_bits(struct regmap *map, unsigned int reg, return regmap_write(map, reg, tmp); } +/** + * regmap_field_read(): Read a value to a single register field + * + * @field: Register field to read from + * @val: Pointer to store read value + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_field_read(struct regmap_field *field, unsigned int *val) +{ + int ret; + unsigned int reg_val; + ret = regmap_read(field->regmap, field->reg, ®_val); + if (ret != 0) + return ret; + + reg_val &= field->mask; + reg_val >>= field->shift; + *val = reg_val; + + return ret; +} + /** * regmap_bulk_read(): Read data from the device * @@ -377,6 +401,67 @@ static int regmap_round_val_bytes(struct regmap *map) return map->format.val_bytes ?: 1; } +/** + * regmap_update_bits_base() - Perform a read/modify/write cycle on a register + * + * @map: Register map to update + * @reg: Register to update + * @mask: Bitmask to change + * @val: New value for bitmask + * @change: Boolean indicating if a write was done + * @async: Boolean indicating asynchronously + * @force: Boolean indicating use force update + * + * Perform a read/modify/write cycle on a register map with change, async, force + * options. + * + * If async is true: + * + * With most buses the read must be done synchronously so this is most useful + * for devices with a cache which do not need to interact with the hardware to + * determine the current register value. + * + * Returns zero for success, a negative number on error. + */ +int regmap_update_bits_base(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force) +{ + int ret; + + ret = regmap_update_bits(map, reg, mask, val); + + return ret; +} + +/** + * regmap_field_update_bits_base() - Perform a read/modify/write cycle a + * register field. + * + * @field: Register field to write to + * @mask: Bitmask to change + * @val: Value to be written + * @change: Boolean indicating if a write was done + * @async: Boolean indicating asynchronously + * @force: Boolean indicating use force update + * + * Perform a read/modify/write cycle on the register field with change, + * async, force option. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_field_update_bits_base(struct regmap_field *field, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force) +{ + mask = (mask << field->shift) & field->mask; + + return regmap_update_bits_base(field->regmap, field->reg, + mask, val << field->shift, + change, async, force); +} + static ssize_t regmap_cdev_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, unsigned long flags) { @@ -442,6 +527,52 @@ size_t regmap_size_bytes(struct regmap *map) return regmap_round_val_bytes(map) * regmap_count_registers(map); } +static void regmap_field_init(struct regmap_field *rm_field, + struct regmap *regmap, struct reg_field reg_field) +{ + rm_field->regmap = regmap; + rm_field->reg = reg_field.reg; + rm_field->shift = reg_field.lsb; + rm_field->mask = GENMASK(reg_field.msb, reg_field.lsb); + + WARN_ONCE(rm_field->mask == 0, "invalid empty mask defined\n"); + + rm_field->id_size = reg_field.id_size; + rm_field->id_offset = reg_field.id_offset; +} + +/** + * regmap_field_bulk_alloc() - Allocate and initialise a bulk register field. + * + * @regmap: regmap bank in which this register field is located. + * @rm_field: regmap register fields within the bank. + * @reg_field: Register fields within the bank. + * @num_fields: Number of register fields. + * + * The return value will be an -ENOMEM on error or zero for success. + * Newly allocated regmap_fields should be freed by calling + * regmap_field_bulk_free() + */ +int regmap_field_bulk_alloc(struct regmap *regmap, + struct regmap_field **rm_field, + const struct reg_field *reg_field, + int num_fields) +{ + struct regmap_field *rf; + int i; + + rf = kcalloc(num_fields, sizeof(*rf), GFP_KERNEL); + if (!rf) + return -ENOMEM; + + for (i = 0; i < num_fields; i++) { + regmap_field_init(&rf[i], regmap, reg_field[i]); + rm_field[i] = &rf[i]; + } + + return 0; +} + /* * regmap_register_cdev - register a devfs file for a regmap * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 3ba0f852f6..e38b4f2dc8 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -212,6 +212,45 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, #define regmap_bulk_write regmap_bulk_write int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count); + +struct regmap_field; + +struct reg_field { + unsigned int reg; + unsigned int lsb; + unsigned int msb; + unsigned int id_size; + unsigned int id_offset; +}; + +#define REG_FIELD(_reg, _lsb, _msb) { \ + .reg = _reg, \ + .lsb = _lsb, \ + .msb = _msb, \ + } + +int regmap_field_bulk_alloc(struct regmap *regmap, + struct regmap_field **rm_field, + const struct reg_field *reg_field, + int num_fields); + +int regmap_field_read(struct regmap_field *field, unsigned int *val); + +int regmap_update_bits_base(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force); + +int regmap_field_update_bits_base(struct regmap_field *field, + unsigned int mask, unsigned int val, + bool *change, bool async, bool force); + +static inline int regmap_field_write(struct regmap_field *field, + unsigned int val) +{ + return regmap_field_update_bits_base(field, ~0, val, + NULL, false, false); +} + #endif int regmap_get_val_bytes(struct regmap *map); -- 2.39.5