Message ID | 20200527125208.24881-8-p.yadav@ti.com |
---|---|
State | New |
Headers | show |
Series | regmap: Add managed API, regmap fields, regmap config | expand |
Hi Pratyush, On Wed, 27 May 2020 at 06:52, Pratyush Yadav <p.yadav at ti.com> wrote: > > From: Jean-Jacques Hiblot <jjhiblot at ti.com> > > A regmap field is an abstraction available in Linux. It provides to access > bitfields in a regmap without having to worry about shifts and masks. > > Signed-off-by: Jean-Jacques Hiblot <jjhiblot at ti.com> > Reviewed-by: Simon Glass <sjg at chromium.org> > Signed-off-by: Pratyush Yadav <p.yadav at ti.com> > --- > drivers/core/regmap.c | 78 ++++++++++++++++++++++++++++++ > include/regmap.h | 108 ++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 186 insertions(+) > Reviewed-by: Simon Glass <sjg at chromium.org> Please see below > diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c > index 4792067f24..cbc01b689a 100644 > --- a/drivers/core/regmap.c > +++ b/drivers/core/regmap.c > @@ -18,6 +18,15 @@ > #include <linux/ioport.h> > #include <linux/compat.h> > #include <linux/err.h> > +#include <linux/bitops.h> > + > +struct regmap_field { Needs comments as I'm not sure what this is for > + struct regmap *regmap; > + unsigned int mask; > + /* lsb */ > + unsigned int shift; > + unsigned int reg; > +}; > > DECLARE_GLOBAL_DATA_PTR; > > @@ -545,3 +554,72 @@ int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val) > > return regmap_write(map, offset, reg | (val & mask)); > } > + > +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; > +} > + > +int regmap_field_write(struct regmap_field *field, unsigned int val) > +{ > + return regmap_update_bits(field->regmap, field->reg, field->mask, > + val << field->shift); > +} > + > +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); > +} > + > +struct regmap_field *devm_regmap_field_alloc(struct udevice *dev, > + struct regmap *regmap, > + struct reg_field reg_field) > +{ > + struct regmap_field *rm_field = devm_kzalloc(dev, sizeof(*rm_field), > + GFP_KERNEL); > + if (!rm_field) > + return ERR_PTR(-ENOMEM); > + > + regmap_field_init(rm_field, regmap, reg_field); > + > + return rm_field; > +} > + > +void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field) > +{ > + devm_kfree(dev, field); > +} > + > +struct regmap_field *regmap_field_alloc(struct regmap *regmap, > + struct reg_field reg_field) > +{ > + struct regmap_field *rm_field = kzalloc(sizeof(*rm_field), GFP_KERNEL); > + > + if (!rm_field) > + return ERR_PTR(-ENOMEM); > + > + regmap_field_init(rm_field, regmap, reg_field); > + > + return rm_field; > +} > + > +void regmap_field_free(struct regmap_field *field) > +{ > + kfree(field); > +} > diff --git a/include/regmap.h b/include/regmap.h > index 007e6f4b6f..190ea44f6a 100644 > --- a/include/regmap.h > +++ b/include/regmap.h > @@ -314,6 +314,43 @@ int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset, > regmap_read_poll_timeout_test(map, addr, val, cond, sleep_us, \ > timeout_ms, 0) \ > > +/** > + * regmap_field_read_poll_timeout - Poll until a condition is met or a timeout > + * occurs > + * > + * @field: Regmap field to read from > + * @val: Unsigned integer variable to read the value into > + * @cond: Break condition (usually involving @val) > + * @sleep_us: Maximum time to sleep between reads in us (0 tight-loops). > + * @timeout_ms: Timeout in ms, 0 means never timeout > + * > + * Returns 0 on success and -ETIMEDOUT upon a timeout or the regmap_field_read > + * error return value in case of a error read. In the two former cases, > + * the last read value at @addr is stored in @val. > + * > + * This is modelled after the regmap_read_poll_timeout macros in linux but > + * with millisecond timeout. > + */ > +#define regmap_field_read_poll_timeout(field, val, cond, sleep_us, timeout_ms) \ > +({ \ > + unsigned long __start = get_timer(0); \ > + int __ret; \ > + for (;;) { \ > + __ret = regmap_field_read((field), &(val)); \ > + if (__ret) \ > + break; \ > + if (cond) \ > + break; \ > + if ((timeout_ms) && get_timer(__start) > (timeout_ms)) { \ > + __ret = regmap_field_read((field), &(val)); \ > + break; \ > + } \ > + if ((sleep_us)) \ > + udelay((sleep_us)); \ > + } \ > + __ret ?: ((cond) ? 0 : -ETIMEDOUT); \ > +}) > + > /** > * regmap_update_bits() - Perform a read/modify/write using a mask > * > @@ -409,4 +446,75 @@ void *regmap_get_range(struct regmap *map, unsigned int range_num); > */ > int regmap_uninit(struct regmap *map); > > +/** > + * struct reg_field - Description of an register field > + * > + * @reg: Offset of the register within the regmap bank > + * @lsb: lsb of the register field. > + * @msb: msb of the register field. > + * @id_size: port size if it has some ports > + * @id_offset: address offset for each ports Those last two don't seem to be present. > + */ > +struct reg_field { > + unsigned int reg; > + unsigned int lsb; > + unsigned int msb; > +}; > + > +struct regmap_field; > + > +#define REG_FIELD(_reg, _lsb, _msb) { \ comment and perhaps an example of how to use it > + .reg = _reg, \ > + .lsb = _lsb, \ > + .msb = _msb, \ > + } > + > +/** > + * devm_regmap_field_alloc() - Allocate and initialise a register field. > + * > + * @dev: Device that will be interacted with > + * @regmap: regmap bank in which this register field is located. > + * @reg_field: Register field with in the bank. > + * > + * The return value will be an ERR_PTR() on error or a valid pointer > + * to a struct regmap_field. The regmap_field will be automatically freed > + * by the device management code. > + */ > +struct regmap_field *devm_regmap_field_alloc(struct udevice *dev, > + struct regmap *regmap, > + struct reg_field reg_field); > +/** > + * devm_regmap_field_free() - Free a register field allocated using > + * devm_regmap_field_alloc. > + * > + * @dev: Device that will be interacted with > + * @field: regmap field which should be freed. > + * > + * Free register field allocated using devm_regmap_field_alloc(). Usually > + * drivers need not call this function, as the memory allocated via devm > + * will be freed as per device-driver life-cyle. > + */ > +void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field); > + > +/** > + * regmap_field_write() - Write a value to a regmap field > + * > + * @field: Regmap field to write to > + * @val: Data to write to the regmap at the specified offset > + * > + * Return: 0 if OK, -ve on error > + */ > +int regmap_field_write(struct regmap_field *field, unsigned int val); > + > +/** > + * regmap_read() - Read a 32-bit value from a regmap > + * > + * @field: Regmap field to write to > + * @valp: Pointer to the buffer to receive the data read from the regmap > + * field > + * > + * Return: 0 if OK, -ve on error > + */ > +int regmap_field_read(struct regmap_field *field, unsigned int *val); > + > #endif > -- > 2.26.2 > Regards, Simon
diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index 4792067f24..cbc01b689a 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -18,6 +18,15 @@ #include <linux/ioport.h> #include <linux/compat.h> #include <linux/err.h> +#include <linux/bitops.h> + +struct regmap_field { + struct regmap *regmap; + unsigned int mask; + /* lsb */ + unsigned int shift; + unsigned int reg; +}; DECLARE_GLOBAL_DATA_PTR; @@ -545,3 +554,72 @@ int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val) return regmap_write(map, offset, reg | (val & mask)); } + +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; +} + +int regmap_field_write(struct regmap_field *field, unsigned int val) +{ + return regmap_update_bits(field->regmap, field->reg, field->mask, + val << field->shift); +} + +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); +} + +struct regmap_field *devm_regmap_field_alloc(struct udevice *dev, + struct regmap *regmap, + struct reg_field reg_field) +{ + struct regmap_field *rm_field = devm_kzalloc(dev, sizeof(*rm_field), + GFP_KERNEL); + if (!rm_field) + return ERR_PTR(-ENOMEM); + + regmap_field_init(rm_field, regmap, reg_field); + + return rm_field; +} + +void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field) +{ + devm_kfree(dev, field); +} + +struct regmap_field *regmap_field_alloc(struct regmap *regmap, + struct reg_field reg_field) +{ + struct regmap_field *rm_field = kzalloc(sizeof(*rm_field), GFP_KERNEL); + + if (!rm_field) + return ERR_PTR(-ENOMEM); + + regmap_field_init(rm_field, regmap, reg_field); + + return rm_field; +} + +void regmap_field_free(struct regmap_field *field) +{ + kfree(field); +} diff --git a/include/regmap.h b/include/regmap.h index 007e6f4b6f..190ea44f6a 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -314,6 +314,43 @@ int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset, regmap_read_poll_timeout_test(map, addr, val, cond, sleep_us, \ timeout_ms, 0) \ +/** + * regmap_field_read_poll_timeout - Poll until a condition is met or a timeout + * occurs + * + * @field: Regmap field to read from + * @val: Unsigned integer variable to read the value into + * @cond: Break condition (usually involving @val) + * @sleep_us: Maximum time to sleep between reads in us (0 tight-loops). + * @timeout_ms: Timeout in ms, 0 means never timeout + * + * Returns 0 on success and -ETIMEDOUT upon a timeout or the regmap_field_read + * error return value in case of a error read. In the two former cases, + * the last read value at @addr is stored in @val. + * + * This is modelled after the regmap_read_poll_timeout macros in linux but + * with millisecond timeout. + */ +#define regmap_field_read_poll_timeout(field, val, cond, sleep_us, timeout_ms) \ +({ \ + unsigned long __start = get_timer(0); \ + int __ret; \ + for (;;) { \ + __ret = regmap_field_read((field), &(val)); \ + if (__ret) \ + break; \ + if (cond) \ + break; \ + if ((timeout_ms) && get_timer(__start) > (timeout_ms)) { \ + __ret = regmap_field_read((field), &(val)); \ + break; \ + } \ + if ((sleep_us)) \ + udelay((sleep_us)); \ + } \ + __ret ?: ((cond) ? 0 : -ETIMEDOUT); \ +}) + /** * regmap_update_bits() - Perform a read/modify/write using a mask * @@ -409,4 +446,75 @@ void *regmap_get_range(struct regmap *map, unsigned int range_num); */ int regmap_uninit(struct regmap *map); +/** + * struct reg_field - Description of an register field + * + * @reg: Offset of the register within the regmap bank + * @lsb: lsb of the register field. + * @msb: msb of the register field. + * @id_size: port size if it has some ports + * @id_offset: address offset for each ports + */ +struct reg_field { + unsigned int reg; + unsigned int lsb; + unsigned int msb; +}; + +struct regmap_field; + +#define REG_FIELD(_reg, _lsb, _msb) { \ + .reg = _reg, \ + .lsb = _lsb, \ + .msb = _msb, \ + } + +/** + * devm_regmap_field_alloc() - Allocate and initialise a register field. + * + * @dev: Device that will be interacted with + * @regmap: regmap bank in which this register field is located. + * @reg_field: Register field with in the bank. + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap_field. The regmap_field will be automatically freed + * by the device management code. + */ +struct regmap_field *devm_regmap_field_alloc(struct udevice *dev, + struct regmap *regmap, + struct reg_field reg_field); +/** + * devm_regmap_field_free() - Free a register field allocated using + * devm_regmap_field_alloc. + * + * @dev: Device that will be interacted with + * @field: regmap field which should be freed. + * + * Free register field allocated using devm_regmap_field_alloc(). Usually + * drivers need not call this function, as the memory allocated via devm + * will be freed as per device-driver life-cyle. + */ +void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field); + +/** + * regmap_field_write() - Write a value to a regmap field + * + * @field: Regmap field to write to + * @val: Data to write to the regmap at the specified offset + * + * Return: 0 if OK, -ve on error + */ +int regmap_field_write(struct regmap_field *field, unsigned int val); + +/** + * regmap_read() - Read a 32-bit value from a regmap + * + * @field: Regmap field to write to + * @valp: Pointer to the buffer to receive the data read from the regmap + * field + * + * Return: 0 if OK, -ve on error + */ +int regmap_field_read(struct regmap_field *field, unsigned int *val); + #endif