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.ha...@pengutronix.de>
---
 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, &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


Reply via email to